summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt2
-rw-r--r--core/api/system-current.txt2
-rw-r--r--core/java/android/app/Notification.java8
-rw-r--r--core/java/android/app/appfunctions/AppFunctionManager.java11
-rw-r--r--core/java/android/app/supervision/flags.aconfig8
-rw-r--r--core/java/android/content/ContentResolver.java4
-rw-r--r--core/java/android/content/pm/RegisteredServicesCache.java13
-rw-r--r--core/java/android/content/pm/multiuser.aconfig10
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java6
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java6
-rw-r--r--core/java/android/hardware/input/input_framework.aconfig10
-rw-r--r--core/java/android/inputmethodservice/NavigationBarController.java22
-rw-r--r--core/java/android/inputmethodservice/navigationbar/NavigationBarView.java2
-rw-r--r--core/java/android/os/BaseBundle.java11
-rw-r--r--core/java/android/os/Bundle.java21
-rw-r--r--core/java/android/os/Parcel.java58
-rw-r--r--core/java/android/os/UserManager.java10
-rw-r--r--core/java/android/provider/Settings.java25
-rw-r--r--core/java/android/service/notification/StatusBarNotification.java7
-rw-r--r--core/java/android/text/StaticLayout.java1
-rw-r--r--core/java/android/text/style/TtsSpan.java22
-rw-r--r--core/java/android/view/IWindowManager.aidl8
-rw-r--r--core/java/android/view/InsetsController.java23
-rw-r--r--core/java/android/view/InsetsSourceControl.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java53
-rw-r--r--core/java/android/view/ViewRootInsetsControllerHost.java7
-rw-r--r--core/java/android/window/DesktopModeFlags.java98
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig9
-rw-r--r--core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java33
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java30
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java2
-rw-r--r--core/java/com/android/internal/widget/LockscreenCredential.java21
-rw-r--r--core/res/res/color-night/surface_effect_0_color.xml20
-rw-r--r--core/res/res/color-night/surface_effect_1_color.xml20
-rw-r--r--core/res/res/color/surface_effect_0_color.xml20
-rw-r--r--core/res/res/color/surface_effect_1_color.xml20
-rw-r--r--core/res/res/drawable/chooser_row_layer_list.xml2
-rw-r--r--core/res/res/values-fa/strings.xml2
-rw-r--r--core/res/res/values-night/colors.xml4
-rw-r--r--core/res/res/values/colors.xml4
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/public-staging.xml8
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java3
-rw-r--r--core/tests/coretests/src/android/window/DesktopModeFlagsTest.java284
-rw-r--r--core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java3
-rw-r--r--data/etc/com.android.systemui.xml1
-rw-r--r--graphics/java/android/graphics/Paint.java8
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java14
-rw-r--r--libs/WindowManager/Shell/aconfig/multitasking.aconfig7
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml4
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStack.kt62
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackController.kt229
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackControllerImpl.kt534
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/automotive/OWNERS6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/automotive/RootTaskStackListener.kt33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java67
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java319
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java54
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java89
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/UserProfileContexts.kt83
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java58
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt54
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt87
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt109
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt78
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IMoveToDesktopCallback.aidl (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoShellModule.java)18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OverviewToDesktopTransitionObserver.kt57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt349
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt34
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/OnDeskRemovedListener.kt22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt182
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java44
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt5
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsLandscape.kt44
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsPortrait.kt44
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarLandscape.kt44
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarPortrait.kt44
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppFromAllApps.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java182
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/UserProfileContextsTest.kt166
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt78
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt121
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt91
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt54
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt13
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/OverviewToDesktopTransitionObserverTest.kt61
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt16
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt342
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt18
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt104
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt256
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java33
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt15
-rw-r--r--libs/hwui/Android.bp3
-rw-r--r--media/java/android/media/MediaRouter2.java2
-rw-r--r--media/java/android/media/flags/projection.aconfig10
-rw-r--r--media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl16
-rw-r--r--media/java/android/media/projection/MediaProjectionEvent.aidl3
-rw-r--r--media/java/android/media/projection/MediaProjectionEvent.java103
-rw-r--r--media/java/android/media/projection/MediaProjectionManager.java21
-rw-r--r--media/java/android/media/quality/MediaQualityManager.java11
-rw-r--r--packages/SettingsLib/BannerMessagePreference/Android.bp4
-rw-r--r--packages/SettingsLib/DisplayUtils/Android.bp2
-rw-r--r--packages/SettingsLib/DisplayUtils/src/com/android/settingslib/display/DisplayDensityConfiguration.java45
-rw-r--r--packages/SettingsLib/Graph/graph.proto8
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt39
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt7
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt89
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt18
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt7
-rw-r--r--packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml2
-rw-r--r--packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt130
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt15
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Bundles.kt46
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt9
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceCoordinate.kt68
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt31
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt7
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt63
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt28
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt5
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt11
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt7
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt13
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-af/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-am/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-ar/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-az/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-be/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-bg/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-bn/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-bs/strings.xml2
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-ca/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-cs/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-da/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-de/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-el/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-en-rAU/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-en-rGB/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-en-rIN/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-es-rUS/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-es/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-et/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-eu/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-fa/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-fi/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-fr-rCA/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-fr/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-gl/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-gu/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-hi/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-hu/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-hy/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-in/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-is/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-it/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-iw/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-ja/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-kk/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-km/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-kn/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-ko/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-ky/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-lv/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-mk/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-ml/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-mn/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-my/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-nb/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-ne/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-nl/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-or/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-pa/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-ro/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-ru/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-sl/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-sq/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-sv/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-sw/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-ta/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-te/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-th/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-tr/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-uk/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-ur/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-uz/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v31/styles_expressive.xml12
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-vi/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-zh-rCN/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-zh-rHK/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-zh-rTW/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-zu/strings.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml12
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java5
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java6
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java116
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java134
-rw-r--r--packages/SystemUI/AndroidManifest.xml10
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp2
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig2
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig41
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt6
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt55
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt73
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/ui/util/SpaceVectorConverter.kt8
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt13
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt21
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/HubOnboardingSection.kt164
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt12
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt36
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt116
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt12
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt66
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt518
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt15
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt9
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt70
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt49
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt19
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt70
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt74
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Overlay.kt4
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Scene.kt4
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/GestureEffect.kt28
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt203
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt22
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt127
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt70
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt75
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java38
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt28
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt34
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/HubOnboardingInteractorTest.kt130
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt66
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/HubOnboardingViewModelTest.kt79
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt155
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelTest.kt84
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java62
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModelTest.kt161
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt56
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/FocusShadeDisplayPolicyTest.kt58
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt65
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt116
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt41
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt130
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerStateTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerStateTest.kt)4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRebindingTrackerTest.kt114
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt67
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/SwitchAppsGestureRecognizerTest.kt85
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureScreenViewModelTest.kt132
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt207
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml11
-rw-r--r--packages/SystemUI/res/drawable-nodpi/hub_onboarding_bg.pngbin0 -> 14127 bytes
-rw-r--r--packages/SystemUI/res/drawable/ic_widgets.xml26
-rw-r--r--packages/SystemUI/res/layout/media_projection_recent_tasks.xml2
-rw-r--r--packages/SystemUI/res/values-af/strings.xml26
-rw-r--r--packages/SystemUI/res/values-am/strings.xml26
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml26
-rw-r--r--packages/SystemUI/res/values-as/strings.xml20
-rw-r--r--packages/SystemUI/res/values-az/strings.xml26
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml20
-rw-r--r--packages/SystemUI/res/values-be/strings.xml26
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml26
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml26
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml24
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml26
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml26
-rw-r--r--packages/SystemUI/res/values-da/strings.xml26
-rw-r--r--packages/SystemUI/res/values-de/strings.xml26
-rw-r--r--packages/SystemUI/res/values-el/strings.xml26
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml26
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml7
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml26
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml26
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml26
-rw-r--r--packages/SystemUI/res/values-es/strings.xml26
-rw-r--r--packages/SystemUI/res/values-et/strings.xml26
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml26
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml30
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml26
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml26
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml26
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml26
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml26
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml26
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml20
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml26
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml26
-rw-r--r--packages/SystemUI/res/values-in/strings.xml26
-rw-r--r--packages/SystemUI/res/values-is/strings.xml26
-rw-r--r--packages/SystemUI/res/values-it/strings.xml26
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml26
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml20
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml26
-rw-r--r--packages/SystemUI/res/values-km/strings.xml26
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml26
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml26
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml26
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml20
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml20
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml26
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml26
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml26
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml26
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml20
-rw-r--r--packages/SystemUI/res/values-my/strings.xml26
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml26
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml26
-rw-r--r--packages/SystemUI/res/values-night/colors.xml10
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml26
-rw-r--r--packages/SystemUI/res/values-or/strings.xml26
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml26
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml20
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml20
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml20
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml26
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml26
-rw-r--r--packages/SystemUI/res/values-si/strings.xml20
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml20
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml26
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml26
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml20
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml26
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml26
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml26
-rw-r--r--packages/SystemUI/res/values-te/strings.xml26
-rw-r--r--packages/SystemUI/res/values-th/strings.xml26
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml20
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml26
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml26
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml26
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml26
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml26
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml28
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml26
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml26
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml26
-rw-r--r--packages/SystemUI/res/values/colors.xml10
-rw-r--r--packages/SystemUI/res/values/dimens.xml5
-rw-r--r--packages/SystemUI/res/values/strings.xml16
-rw-r--r--packages/SystemUI/res/values/styles.xml1
-rw-r--r--packages/SystemUI/res/xml/gradient_color_wallpaper.xml20
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt30
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java40
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/shared/colors/ShadeColors.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/shared/colors/SurfaceEffectColors.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/DevicePosturingCommandListener.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt103
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/HubOnboardingInteractor.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/dagger/NoopPosturingModule.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/HubOnboardingViewModel.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/grid/ui/compose/SpannedGrids.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt119
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt190
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModel.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotCrossProfileService.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayChangeLatencyTracker.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/FakeShadeDisplayPolicy.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/FocusShadeDisplayPolicy.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt149
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/ShadeColors.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt77
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java74
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt)37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationStackModule.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java100
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRebindingTracker.kt109
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackRebindingHider.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/SwitchAppsGestureRecognizer.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureRecognizerProvider.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/GradientColorWallpaper.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java131
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/HubOnboardingInteractorKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt35
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/PosturingRepositoryKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeUserActionsViewModelKosmos.kt)9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/HubOnboardingViewModelKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeFocusedDisplayRepository.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigKosmos.kt35
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelFactoryKosmos.kt30
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt17
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt43
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/NotificationRebindingTrackerKosmos.kt30
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerKosmos.kt3
-rw-r--r--ravenwood/Android.bp7
-rw-r--r--ravenwood/Framework.bp52
-rw-r--r--ravenwood/tools/ravenizer/Android.bp2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AutoclickController.java149
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java24
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java76
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java45
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java1
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java4
-rw-r--r--services/core/java/com/android/server/backup/SystemBackupAgent.java7
-rw-r--r--services/core/java/com/android/server/display/DisplayBackupHelper.java137
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java90
-rw-r--r--services/core/java/com/android/server/display/DisplayTopologyCoordinator.java120
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java49
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecNetwork.java3
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java6
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java46
-rw-r--r--services/core/java/com/android/server/media/AudioManagerRouteController.java56
-rw-r--r--services/core/java/com/android/server/media/DeviceRouteController.java12
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java40
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java102
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionStopController.java5
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java9
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java90
-rw-r--r--services/core/java/com/android/server/notification/ZenLog.java5
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig10
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java8
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java63
-rw-r--r--services/core/java/com/android/server/policy/SingleKeyGestureDetector.java15
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java29
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java24
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java2
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java4
-rw-r--r--services/core/java/com/android/server/wm/LaunchParamsController.java2
-rw-r--r--services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java7
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java51
-rw-r--r--services/core/jni/com_android_server_utils_AnrTimer.cpp10
-rw-r--r--services/java/com/android/server/SystemServer.java2
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayBackupHelperTest.kt86
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java146
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt20
-rw-r--r--services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/SystemBackupAgentTest.java12
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java33
-rw-r--r--services/tests/security/intrusiondetection/AndroidTest.xml2
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java20
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java128
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java46
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java1
-rw-r--r--telephony/java/android/telephony/PreciseDisconnectCause.java7
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java4
-rw-r--r--tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java11
-rwxr-xr-xtools/aapt2/tools/finalize_res.py111
580 files changed, 12261 insertions, 6344 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index e0fc9590f9f7..6964866db7f3 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -45145,7 +45145,7 @@ package android.telephony {
field public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
field public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL = "show_ims_registration_status_bool";
field public static final String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
- field @FlaggedApi("com.android.internal.telephony.flags.hide_roaming_icon") public static final String KEY_SHOW_ROAMING_INDICATOR_BOOL = "show_roaming_indicator_bool";
+ field public static final String KEY_SHOW_ROAMING_INDICATOR_BOOL = "show_roaming_indicator_bool";
field public static final String KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL = "show_signal_strength_in_sim_status_bool";
field public static final String KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL = "show_video_call_charges_alert_dialog_bool";
field public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL = "show_wfc_location_privacy_policy_bool";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b705d3c7cd55..937a9ffaf210 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -15450,6 +15450,8 @@ package android.telephony {
field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64
field public static final int DESTINATION_OUT_OF_ORDER = 27; // 0x1b
field @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public static final int EMERGENCY_PERM_FAILURE = 326; // 0x146
+ field @FlaggedApi("com.android.internal.telephony.flags.add_ims_redial_codes_for_emergency_calls") public static final int EMERGENCY_REDIAL_ON_IMS = 3001; // 0xbb9
+ field @FlaggedApi("com.android.internal.telephony.flags.add_ims_redial_codes_for_emergency_calls") public static final int EMERGENCY_REDIAL_ON_VOWIFI = 3002; // 0xbba
field @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public static final int EMERGENCY_TEMP_FAILURE = 325; // 0x145
field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff
field public static final int FACILITY_REJECTED = 29; // 0x1d
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index fcb817ede6b3..35308ee43dea 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4393,6 +4393,9 @@ public class Notification implements Parcelable
*/
@Nullable
public Pair<RemoteInput, Action> findRemoteInputActionPair(boolean requiresFreeform) {
+ if (isPromotedOngoing()) {
+ return null;
+ }
if (actions == null) {
return null;
}
@@ -6454,6 +6457,11 @@ public class Notification implements Parcelable
if (mActions == null) return Collections.emptyList();
List<Notification.Action> standardActions = new ArrayList<>();
for (Notification.Action action : mActions) {
+ // Actions with RemoteInput are ignored for RONs.
+ if (mN.isPromotedOngoing()
+ && hasValidRemoteInput(action)) {
+ continue;
+ }
if (!action.isContextual()) {
standardActions.add(action);
}
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index a731e5085466..6fd8db995368 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -42,12 +42,13 @@ import java.util.Objects;
import java.util.concurrent.Executor;
/**
- * Provides access to app functions.
+ * Provides access to App Functions. App Functions is currently a
+ * beta/experimental preview feature.
*
* <p>An app function is a piece of functionality that apps expose to the system for cross-app
* orchestration.
*
- * <p>**Building App Functions:**
+ * <h3>Building App Functions</h3>
*
* <p>Most developers should build app functions through the AppFunctions SDK. This SDK library
* offers a more convenient and type-safe way to build app functions. The SDK provides predefined
@@ -56,7 +57,7 @@ import java.util.concurrent.Executor;
* these data classes into {@link ExecuteAppFunctionRequest#getParameters()} and {@link
* ExecuteAppFunctionResponse#getResultDocument()}.
*
- * <p>**Discovering App Functions:**
+ * <h3>Discovering App Functions</h3>
*
* <p>When there is a package change or the device starts up, the metadata of available functions is
* indexed on-device by {@link AppSearchManager}. AppSearch stores the indexed information as an
@@ -66,7 +67,7 @@ import java.util.concurrent.Executor;
* document is based on the packages that have visibility to the app providing the app functions.
* AppFunction SDK provides a convenient way to achieve this and is the preferred method.
*
- * <p>**Executing App Functions:**
+ * <h3>Executing App Functions</h3>
*
* <p>To execute an app function, the caller app can retrieve the {@code functionIdentifier} from
* the {@code AppFunctionStaticMetadata} document and use it to build an {@link
@@ -76,7 +77,7 @@ import java.util.concurrent.Executor;
* apps. An app can always execute its own app functions and doesn't need these permissions.
* AppFunction SDK provides a convenient way to achieve this and is the preferred method.
*
- * <p>**Example:**
+ * <h3>Example</h3>
*
* <p>An assistant app is trying to fulfill the user request "Save XYZ into my note". The assistant
* app should first list all available app functions as {@code AppFunctionStaticMetadata} documents
diff --git a/core/java/android/app/supervision/flags.aconfig b/core/java/android/app/supervision/flags.aconfig
index 18182b804627..19fdbb7214d0 100644
--- a/core/java/android/app/supervision/flags.aconfig
+++ b/core/java/android/app/supervision/flags.aconfig
@@ -48,3 +48,11 @@ flag {
description: "Flag that enables the Supervision settings screen with top-level Android settings entry point"
bug: "383404606"
}
+
+flag {
+ name: "enable_app_approval"
+ is_exported: true
+ namespace: "supervision"
+ description: "Flag to enable the App Approval settings in Android settings UI"
+ bug: "390185393"
+}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index a126363237b8..efcaa0ea6f07 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -2722,10 +2722,10 @@ public abstract class ContentResolver implements ContentInterface {
/** @hide - designated user version */
@UnsupportedAppUsage
- public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
+ public final void registerContentObserver(Uri uri, boolean notifyForDescendants,
ContentObserver observer, @UserIdInt int userHandle) {
try {
- getContentService().registerContentObserver(uri, notifyForDescendents,
+ getContentService().registerContentObserver(uri, notifyForDescendants,
observer.getContentObserver(), userHandle, mTargetSdkVersion);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 82663849f316..74da62c85ed2 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -527,13 +527,14 @@ public abstract class RegisteredServicesCache<V> {
lastUpdateTime = packageInfo.lastUpdateTime;
} catch (NameNotFoundException | SecurityException e) {
Slog.d(TAG, "Fail to get the PackageInfo in generateServicesMap: " + e);
- continue;
}
- ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(componentName,
- lastUpdateTime);
- if (serviceInfo != null) {
- serviceInfos.add(serviceInfo);
- continue;
+ if (lastUpdateTime >= 0) {
+ ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(componentName,
+ lastUpdateTime);
+ if (serviceInfo != null) {
+ serviceInfos.add(serviceInfo);
+ continue;
+ }
}
}
try {
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 4a579a4c0e85..4e6fb8d3a8e7 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -500,6 +500,16 @@ flag {
}
flag {
+ name: "get_user_switchability_permission"
+ namespace: "multiuser"
+ description: "Update permissions for getUserSwitchability"
+ bug: "390458180"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "restrict_quiet_mode_credential_bug_fix_to_managed_profiles"
namespace: "profile_experiences"
description: "Use user states to check the state of quiet mode for managed profiles only"
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 53813012b4b3..71d0a04760ac 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -541,9 +541,9 @@ public class ApkLiteParseUtils {
case TAG_USES_SDK_LIBRARY:
String usesSdkLibName = parser.getAttributeValue(
ANDROID_RES_NAMESPACE, "name");
- // TODO(b/379219371): Due to a bug in bundletool, some apps can use
- // versionMajor as string. Until it is resolved, we are adding a
- // workaround here.
+ // TODO(b/391604666): Due to a bug in bundletool, old apps could be
+ // using versionMajor as string. Do not remove this workaround until
+ // b/391604666 is resolved.
String usesSdkLibVersionMajorString = parser.getAttributeValue(
ANDROID_RES_NAMESPACE, "versionMajor");
long usesSdkLibVersionMajor = XmlUtils.convertValueToInt(
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index d273ddb15cc4..343e4b5668c0 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -475,6 +475,12 @@ public abstract class DisplayManagerInternal {
*/
public abstract boolean isDisplayReadyForMirroring(int displayId);
+ /**
+ * Called by {@link com.android.server.display.DisplayBackupHelper} when backup files were
+ * restored and are ready to be reloaded.
+ */
+ public abstract void reloadTopologies(int userId);
+
/**
* Used by the window manager to override the per-display screen brightness based on the
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 62126963cba4..79323bf2f2f7 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -225,3 +225,13 @@ flag {
description: "Removes modifiers from the original key event that activated the fallback, ensuring that only the intended fallback event is sent."
bug: "382545048"
}
+
+flag {
+ name: "abort_slow_multi_press"
+ namespace: "wear_frameworks"
+ description: "If a press that's a part of a multipress takes too long, the multipress gesture will be cancelled."
+ bug: "370095426"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 38be8d9f772d..019ba0045916 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -16,6 +16,9 @@
package android.inputmethodservice;
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
@@ -23,7 +26,6 @@ import android.animation.ValueAnimator;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.StatusBarManager;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -241,10 +243,9 @@ final class NavigationBarController {
if (navigationBarView != null) {
// TODO(b/213337792): Support InputMethodService#setBackDisposition().
// TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary.
- final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
+ final int hints = NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
| (mShouldShowImeSwitcherWhenImeIsShown
- ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN
- : 0);
+ ? NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
navigationBarView.setNavigationIconHints(hints);
navigationBarView.prepareNavButtons(this);
}
@@ -512,13 +513,14 @@ final class NavigationBarController {
}
final NavigationBarView navigationBarView = mNavigationBarFrame.findViewByPredicate(
NavigationBarView.class::isInstance);
- if (navigationBarView == null) {
- return;
+ if (navigationBarView != null) {
+ // TODO(b/213337792): Support InputMethodService#setBackDisposition().
+ // TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary.
+ final int hints = NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
+ | (mShouldShowImeSwitcherWhenImeIsShown
+ ? NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
+ navigationBarView.setNavigationIconHints(hints);
}
- final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
- | (shouldShowImeSwitcherWhenImeIsShown
- ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
- navigationBarView.setNavigationIconHints(hints);
} else {
uninstallNavigationBarFrameIfNecessary();
}
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
index 209f323d7b34..e7e46a9482c8 100644
--- a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
@@ -290,7 +290,7 @@ public final class NavigationBarView extends FrameLayout {
final boolean oldBackAlt =
(mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
if (newBackAlt != oldBackAlt) {
- //onImeVisibilityChanged(newBackAlt);
+ //onBackAltChanged(newBackAlt);
}
if (DEBUG) {
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 1041041b2a27..1cf293d46350 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -45,8 +45,7 @@ import java.util.function.BiFunction;
* {@link PersistableBundle} subclass.
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
-@SuppressWarnings("HiddenSuperclass")
-public class BaseBundle implements Parcel.ClassLoaderProvider {
+public class BaseBundle {
/** @hide */
protected static final String TAG = "Bundle";
static final boolean DEBUG = false;
@@ -300,9 +299,8 @@ public class BaseBundle implements Parcel.ClassLoaderProvider {
/**
* Return the ClassLoader currently associated with this Bundle.
- * @hide
*/
- public ClassLoader getClassLoader() {
+ ClassLoader getClassLoader() {
return mClassLoader;
}
@@ -416,9 +414,6 @@ public class BaseBundle implements Parcel.ClassLoaderProvider {
if ((mFlags & Bundle.FLAG_VERIFY_TOKENS_PRESENT) != 0) {
Intent.maybeMarkAsMissingCreatorToken(object);
}
- } else if (object instanceof Bundle) {
- Bundle bundle = (Bundle) object;
- bundle.setClassLoaderSameAsContainerBundleWhenRetrievedFirstTime(this);
}
return (clazz != null) ? clazz.cast(object) : (T) object;
}
@@ -492,7 +487,7 @@ public class BaseBundle implements Parcel.ClassLoaderProvider {
int[] numLazyValues = new int[]{0};
try {
parcelledData.readArrayMap(map, count, !parcelledByNative,
- /* lazy */ ownsParcel, this, numLazyValues);
+ /* lazy */ ownsParcel, mClassLoader, numLazyValues);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 55bfd451d97a..819d58d9f059 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -141,8 +141,6 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
STRIPPED.putInt("STRIPPED", 1);
}
- private boolean isFirstRetrievedFromABundle = false;
-
/**
* Constructs a new, empty Bundle.
*/
@@ -1022,9 +1020,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
return null;
}
try {
- Bundle bundle = (Bundle) o;
- bundle.setClassLoaderSameAsContainerBundleWhenRetrievedFirstTime(this);
- return bundle;
+ return (Bundle) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Bundle", e);
return null;
@@ -1032,21 +1028,6 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}
/**
- * Set the ClassLoader of a bundle to its container bundle. This is necessary so that when a
- * bundle's ClassLoader is changed, it can be propagated to its children. Do this only when it
- * is retrieved from the container bundle first time though. Once it is accessed outside of its
- * container, its ClassLoader should no longer be changed by its container anymore.
- *
- * @param containerBundle the bundle this bundle is retrieved from.
- */
- void setClassLoaderSameAsContainerBundleWhenRetrievedFirstTime(BaseBundle containerBundle) {
- if (!isFirstRetrievedFromABundle) {
- setClassLoader(containerBundle.getClassLoader());
- isFirstRetrievedFromABundle = true;
- }
- }
-
- /**
* Returns the value associated with the given key, or {@code null} if
* no mapping of the desired type exists for the given key or a {@code null}
* value is explicitly associated with the key.
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 3c4139d39762..e58934746c14 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -4661,7 +4661,7 @@ public final class Parcel {
* @hide
*/
@Nullable
- private Object readLazyValue(@Nullable ClassLoaderProvider loaderProvider) {
+ public Object readLazyValue(@Nullable ClassLoader loader) {
int start = dataPosition();
int type = readInt();
if (isLengthPrefixed(type)) {
@@ -4672,17 +4672,12 @@ public final class Parcel {
int end = MathUtils.addOrThrow(dataPosition(), objectLength);
int valueLength = end - start;
setDataPosition(end);
- return new LazyValue(this, start, valueLength, type, loaderProvider);
+ return new LazyValue(this, start, valueLength, type, loader);
} else {
- return readValue(type, getClassLoader(loaderProvider), /* clazz */ null);
+ return readValue(type, loader, /* clazz */ null);
}
}
- @Nullable
- private static ClassLoader getClassLoader(@Nullable ClassLoaderProvider loaderProvider) {
- return loaderProvider == null ? null : loaderProvider.getClassLoader();
- }
-
private static final class LazyValue implements BiFunction<Class<?>, Class<?>[], Object> {
/**
@@ -4696,12 +4691,7 @@ public final class Parcel {
private final int mPosition;
private final int mLength;
private final int mType;
- // this member is set when a bundle that includes a LazyValue is unparceled. But it is used
- // when apply method is called. Between these 2 events, the bundle's ClassLoader could have
- // changed. Let the bundle be a ClassLoaderProvider allows the bundle provides its current
- // ClassLoader at the time apply method is called.
- @NonNull
- private final ClassLoaderProvider mLoaderProvider;
+ @Nullable private final ClassLoader mLoader;
@Nullable private Object mObject;
/**
@@ -4712,13 +4702,12 @@ public final class Parcel {
*/
@Nullable private volatile Parcel mSource;
- LazyValue(Parcel source, int position, int length, int type,
- @NonNull ClassLoaderProvider loaderProvider) {
+ LazyValue(Parcel source, int position, int length, int type, @Nullable ClassLoader loader) {
mSource = requireNonNull(source);
mPosition = position;
mLength = length;
mType = type;
- mLoaderProvider = loaderProvider;
+ mLoader = loader;
}
@Override
@@ -4731,8 +4720,7 @@ public final class Parcel {
int restore = source.dataPosition();
try {
source.setDataPosition(mPosition);
- mObject = source.readValue(mLoaderProvider.getClassLoader(), clazz,
- itemTypes);
+ mObject = source.readValue(mLoader, clazz, itemTypes);
} finally {
source.setDataPosition(restore);
}
@@ -4805,8 +4793,7 @@ public final class Parcel {
return Objects.equals(mObject, value.mObject);
}
// Better safely fail here since this could mean we get different objects.
- if (!Objects.equals(mLoaderProvider.getClassLoader(),
- value.mLoaderProvider.getClassLoader())) {
+ if (!Objects.equals(mLoader, value.mLoader)) {
return false;
}
// Otherwise compare metadata prior to comparing payload.
@@ -4820,24 +4807,10 @@ public final class Parcel {
@Override
public int hashCode() {
// Accessing mSource first to provide memory barrier for mObject
- return Objects.hash(mSource == null, mObject, mLoaderProvider.getClassLoader(), mType,
- mLength);
+ return Objects.hash(mSource == null, mObject, mLoader, mType, mLength);
}
}
- /**
- * Provides a ClassLoader.
- * @hide
- */
- public interface ClassLoaderProvider {
- /**
- * Returns a ClassLoader.
- *
- * @return ClassLoader
- */
- ClassLoader getClassLoader();
- }
-
/** Same as {@link #readValue(ClassLoader, Class, Class[])} without any item types. */
private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
// Avoids allocating Class[0] array
@@ -5578,8 +5551,8 @@ public final class Parcel {
}
private void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
- int size, @Nullable ClassLoaderProvider loaderProvider) {
- readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loaderProvider, null);
+ int size, @Nullable ClassLoader loader) {
+ readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader, null);
}
/**
@@ -5593,12 +5566,11 @@ public final class Parcel {
* @hide
*/
void readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
- boolean lazy, @Nullable ClassLoaderProvider loaderProvider, int[] lazyValueCount) {
+ boolean lazy, @Nullable ClassLoader loader, int[] lazyValueCount) {
ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, size);
while (size > 0) {
String key = readString();
- Object value = (lazy) ? readLazyValue(loaderProvider) : readValue(
- getClassLoader(loaderProvider));
+ Object value = (lazy) ? readLazyValue(loader) : readValue(loader);
if (value instanceof LazyValue) {
lazyValueCount[0]++;
}
@@ -5619,12 +5591,12 @@ public final class Parcel {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void readArrayMap(@NonNull ArrayMap<? super String, Object> outVal,
- @Nullable ClassLoaderProvider loaderProvider) {
+ @Nullable ClassLoader loader) {
final int N = readInt();
if (N < 0) {
return;
}
- readArrayMapInternal(outVal, N, loaderProvider);
+ readArrayMapInternal(outVal, N, loader);
}
/**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 08f68f1874e7..967f55ce7a88 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -30,6 +30,9 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SpecialUsers.CanBeALL;
+import android.annotation.SpecialUsers.CanBeNULL;
+import android.annotation.SpecialUsers.CannotBeSpecialUser;
import android.annotation.StringDef;
import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
@@ -3913,7 +3916,8 @@ public class UserManager {
android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.QUERY_USERS,
android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
- public @NonNull UserProperties getUserProperties(@NonNull UserHandle userHandle) {
+ public @NonNull UserProperties getUserProperties(
+ @CannotBeSpecialUser @NonNull UserHandle userHandle) {
final int userId = userHandle.getIdentifier();
if (userId < 0 && android.multiuser.Flags.fixGetUserPropertyCache()) {
@@ -6811,7 +6815,7 @@ public class UserManager {
*/
@SystemApi
public static final class EnforcingUser implements Parcelable {
- private final @UserIdInt int userId;
+ private final @CanBeALL @CanBeNULL @UserIdInt int userId;
private final @UserRestrictionSource int userRestrictionSource;
/**
@@ -6856,7 +6860,7 @@ public class UserManager {
*
* <p> Will be UserHandle.USER_NULL when restriction is set by the system.
*/
- public UserHandle getUserHandle() {
+ public @CanBeALL @CanBeNULL UserHandle getUserHandle() {
return UserHandle.of(userId);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2e231e3957c6..65c857a51b29 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11008,6 +11008,21 @@ public final class Settings {
@Readable
public static final String SHOW_NOTIFICATION_SNOOZE = "show_notification_snooze";
+ /**
+ * Controls whether dual shade is enabled. This splits notifications and quick settings to
+ * have their own independently expandable/collapsible panels, appearing on either side of
+ * the large screen (including unfolded device) or sharing a space on a narrow screen
+ * (including a folded device). Both panels will now cover the screen only partially
+ * (wrapping their content), so a running app or the lockscreen will remain visible in the
+ * background.
+ * <p>
+ * Type: int (0 for false, 1 for true)
+ *
+ * @hide
+ */
+ @android.provider.Settings.Readable
+ public static final String DUAL_SHADE = "dual_shade";
+
/**
* 1 if it is allowed to remove the primary GAIA account. 0 by default.
* @hide
@@ -13788,6 +13803,16 @@ public final class Settings {
= "enable_freeform_support";
/**
+ * Whether to override the availability of the desktop experiences features on the
+ * device. With desktop experiences enabled, secondary displays can be used to run
+ * apps, in desktop mode by default. Otherwise they can only be used for mirroring.
+ * @hide
+ */
+ @Readable
+ public static final String DEVELOPMENT_OVERRIDE_DESKTOP_EXPERIENCE_FEATURES =
+ "override_desktop_experience_features";
+
+ /**
* Whether to override the availability of the desktop mode on the main display of the
* device. If on, users can make move an app to the desktop, allowing a freeform windowing
* experience.
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 105fa3ffd4cd..79957f411597 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -18,6 +18,8 @@ package android.service.notification;
import static android.text.TextUtils.formatSimple;
+import static com.android.window.flags.Flags.enablePerDisplayPackageContextCacheInStatusbarNotif;
+
import android.annotation.NonNull;
import android.app.Notification;
import android.app.NotificationManager;
@@ -37,9 +39,9 @@ import android.util.ArrayMap;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import static com.android.window.flags.Flags.enablePerDisplayPackageContextCacheInStatusbarNotif;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Map;
/**
@@ -81,7 +83,8 @@ public class StatusBarNotification implements Parcelable {
@Deprecated
private Context mContext; // used for inflation & icon expansion
// Maps display id to context used for remote view content inflation and status bar icon.
- private final Map<Integer, Context> mContextForDisplayId = new ArrayMap<>();
+ private final Map<Integer, Context> mContextForDisplayId =
+ Collections.synchronizedMap(new ArrayMap<>());
/** @hide */
public StatusBarNotification(String pkg, String opPkg, int id,
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index cb498503f201..a5d52957c40e 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -121,6 +121,7 @@ public class StaticLayout extends Layout {
b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
b.mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;
b.mLineBreakConfig = LineBreakConfig.NONE;
+ b.mUseBoundsForWidth = false;
b.mMinimumFontMetrics = null;
return b;
}
diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java
index a337ba2a57fb..e0d4ec1ca826 100644
--- a/core/java/android/text/style/TtsSpan.java
+++ b/core/java/android/text/style/TtsSpan.java
@@ -108,11 +108,13 @@ public class TtsSpan implements ParcelableSpan {
/**
* The text associated with this span is a time, consisting of a number of
- * hours and minutes, specified with {@link #ARG_HOURS} and
- * {@link #ARG_MINUTES}.
+ * hours, minutes, and seconds specified with {@link #ARG_HOURS}, {@link #ARG_MINUTES}, and
+ * {@link #ARG_SECONDS}.
* Also accepts the arguments {@link #ARG_GENDER},
* {@link #ARG_ANIMACY}, {@link #ARG_MULTIPLICITY} and
- * {@link #ARG_CASE}.
+ * {@link #ARG_CASE}. This is different from {@link #TYPE_DURATION}. This should be used to
+ * convey a particular moment in time, such as a clock time, while {@link #TYPE_DURATION} should
+ * be used to convey an interval of time.
*/
public static final String TYPE_TIME = "android.type.time";
@@ -310,16 +312,18 @@ public class TtsSpan implements ParcelableSpan {
public static final String ARG_UNIT = "android.arg.unit";
/**
- * Argument used to specify the hours of a time. The hours should be
- * provided as an integer in the range from 0 up to and including 24.
- * Can be used with {@link #TYPE_TIME}.
+ * Argument used to specify the hours of a time or duration. The hours should be
+ * provided as an integer in the range from 0 up to and including 24 for
+ * {@link #TYPE_TIME}.
+ * Can be used with {@link #TYPE_TIME} or {@link #TYPE_DURATION}.
*/
public static final String ARG_HOURS = "android.arg.hours";
/**
- * Argument used to specify the minutes of a time. The minutes should be
- * provided as an integer in the range from 0 up to and including 59.
- * Can be used with {@link #TYPE_TIME}.
+ * Argument used to specify the minutes of a time or duration. The minutes should be
+ * provided as an integer in the range from 0 up to and including 59 for
+ * {@link #TYPE_TIME}.
+ * Can be used with {@link #TYPE_TIME} or {@link #TYPE_DURATION}.
*/
public static final String ARG_MINUTES = "android.arg.minutes";
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index f58baffb1367..2ec5dbc5612a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -1146,6 +1146,14 @@ interface IWindowManager
*/
KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId);
+ /*
+ * Notifies about IME insets animation.
+ *
+ * @param running Indicates the insets animation state.
+ * @param animationType Indicates the {@link InsetsController.AnimationType}
+ */
+ oneway void notifyImeInsetsAnimationStateChanged(boolean running, int animationType);
+
/**
* Returns whether the display with {@code displayId} ignores orientation request.
*/
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index c174fbe0bbcd..e097a0764512 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -214,9 +214,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
* Notifies when the state of running animation is changed. The state is either "running" or
* "idle".
*
- * @param running {@code true} if there is any animation running; {@code false} otherwise.
+ * @param running {@code true} if the given insets types start running
+ * {@code false} otherwise.
+ * @param animationType {@link AnimationType}
+ * @param insetsTypes {@link Type}.
*/
- default void notifyAnimationRunningStateChanged(boolean running) {}
+ default void notifyAnimationRunningStateChanged(boolean running,
+ @AnimationType int animationType, @InsetsType int insetsTypes) {
+ }
/** @see ViewRootImpl#isHandlingPointerEvent */
default boolean isHandlingPointerEvent() {
@@ -744,9 +749,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
final InsetsAnimationControlRunner runner = new InsetsResizeAnimationRunner(
mFrame, mFromState, mToState, RESIZE_INTERPOLATOR,
ANIMATION_DURATION_RESIZE, mTypes, InsetsController.this);
- if (mRunningAnimations.isEmpty()) {
- mHost.notifyAnimationRunningStateChanged(true);
- }
+ mHost.notifyAnimationRunningStateChanged(true,
+ runner.getAnimationType(), mTypes);
mRunningAnimations.add(new RunningAnimation(runner, runner.getAnimationType()));
}
};
@@ -1560,9 +1564,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
}
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_ANIMATION_RUNNING);
- if (mRunningAnimations.isEmpty()) {
- mHost.notifyAnimationRunningStateChanged(true);
- }
+ mHost.notifyAnimationRunningStateChanged(true, animationType, types);
mRunningAnimations.add(new RunningAnimation(runner, animationType));
if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: "
+ useInsetsAnimationThread);
@@ -1842,9 +1844,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
break;
}
}
- if (mRunningAnimations.isEmpty()) {
- mHost.notifyAnimationRunningStateChanged(false);
- }
+ mHost.notifyAnimationRunningStateChanged(
+ false, control.getAnimationType(), removedTypes);
onAnimationStateChanged(removedTypes, false /* running */);
}
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 7f2f0e8863df..cfb4835a13f7 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -194,7 +194,7 @@ public class InsetsSourceControl implements Parcelable {
}
public void release(Consumer<SurfaceControl> surfaceReleaseConsumer) {
- if (mLeash != null) {
+ if (mLeash != null && mLeash.isValid()) {
surfaceReleaseConsumer.accept(mLeash);
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cd8a85a66c1a..7c5b300e9d24 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -24,6 +24,7 @@ import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED;
import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND;
import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID;
import static android.os.Trace.TRACE_TAG_VIEW;
+import static android.service.autofill.Flags.improveFillDialogAconfig;
import static android.util.SequenceUtils.getInitSeq;
import static android.util.SequenceUtils.isIncomingSeqStale;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -922,6 +923,8 @@ public final class ViewRootImpl implements ViewParent,
private boolean mInsetsAnimationRunning;
+ private int mInsetsAnimatingTypes = 0;
+
private long mPreviousFrameDrawnTime = -1;
// The largest view size percentage to the display size. Used on trace to collect metric.
private float mLargestChildPercentage = 0.0f;
@@ -2520,17 +2523,49 @@ public final class ViewRootImpl implements ViewParent,
* Notify the when the running state of a insets animation changed.
*/
@VisibleForTesting
- public void notifyInsetsAnimationRunningStateChanged(boolean running) {
+ public void notifyInsetsAnimationRunningStateChanged(boolean running,
+ @InsetsController.AnimationType int animationType,
+ @InsetsType int insetsTypes) {
+ @InsetsType int previousInsetsType = mInsetsAnimatingTypes;
+ // If improveFillDialogAconfig is disabled, we notify WindowSession of all the updates we
+ // receive here
+ boolean notifyWindowSession = !improveFillDialogAconfig();
+ if (running) {
+ mInsetsAnimatingTypes |= insetsTypes;
+ } else {
+ mInsetsAnimatingTypes &= ~insetsTypes;
+ }
if (sToolkitSetFrameRateReadOnlyFlagValue) {
- if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
- Trace.instant(Trace.TRACE_TAG_VIEW,
- TextUtils.formatSimple("notifyInsetsAnimationRunningStateChanged(%s)",
- Boolean.toString(running)));
- }
mInsetsAnimationRunning = running;
- try {
- mWindowSession.notifyInsetsAnimationRunningStateChanged(mWindow, running);
- } catch (RemoteException e) {
+ // If improveFillDialogAconfig is enabled, we need to confirm other animations aren't
+ // running to maintain the existing behavior. System server were notified previously
+ // only when animation started running or stopped when there were no running animations.
+ if (improveFillDialogAconfig()) {
+ if ((previousInsetsType == 0 && mInsetsAnimatingTypes != 0)
+ || (previousInsetsType != 0 && mInsetsAnimatingTypes == 0)) {
+ notifyWindowSession = true;
+ }
+ }
+ if (notifyWindowSession) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.instant(Trace.TRACE_TAG_VIEW,
+ TextUtils.formatSimple("notifyInsetsAnimationRunningStateChanged(%s)",
+ Boolean.toString(running)));
+ }
+ try {
+ mWindowSession.notifyInsetsAnimationRunningStateChanged(mWindow, running);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ if (improveFillDialogAconfig()) {
+ // Update WindowManager for ImeAnimation
+ if ((insetsTypes & WindowInsets.Type.ime()) != 0) {
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .notifyImeInsetsAnimationStateChanged(running, animationType);
+ } catch (RemoteException e) {
+ }
}
}
}
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index 889acca4b8b1..f1666dbebd7b 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -275,9 +275,12 @@ public class ViewRootInsetsControllerHost implements InsetsController.Host {
}
@Override
- public void notifyAnimationRunningStateChanged(boolean running) {
+ public void notifyAnimationRunningStateChanged(boolean running,
+ @InsetsController.AnimationType int animationType,
+ @WindowInsets.Type.InsetsType int insetsTypes) {
if (mViewRoot != null) {
- mViewRoot.notifyInsetsAnimationRunningStateChanged(running);
+ mViewRoot.notifyInsetsAnimationRunningStateChanged(
+ running, animationType, insetsTypes);
}
}
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index be69d3da3874..d44b941082b5 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -20,12 +20,13 @@ import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.Application;
import android.content.ContentResolver;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Log;
import com.android.window.flags.Flags;
-import java.util.function.Supplier;
+import java.util.function.BooleanSupplier;
/**
* Checks desktop mode flag state.
@@ -90,9 +91,35 @@ public enum DesktopModeFlags {
INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC(
Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true);
- private static final String TAG = "DesktopModeFlagsUtil";
+ /**
+ * Flag class, to be used in case the enum cannot be used because the flag is not accessible.
+ *
+ * <p> This class will still use the process-wide cache.
+ */
+ public static class DesktopModeFlag {
+ // Function called to obtain aconfig flag value.
+ private final BooleanSupplier mFlagFunction;
+ // Whether the flag state should be affected by developer option.
+ private final boolean mShouldOverrideByDevOption;
+
+ public DesktopModeFlag(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) {
+ this.mFlagFunction = flagFunction;
+ this.mShouldOverrideByDevOption = shouldOverrideByDevOption;
+ }
+
+ /**
+ * Determines state of flag based on the actual flag and desktop mode developer option
+ * overrides.
+ */
+ public boolean isTrue() {
+ return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
+ }
+
+ }
+
+ private static final String TAG = "DesktopModeFlags";
// Function called to obtain aconfig flag value.
- private final Supplier<Boolean> mFlagFunction;
+ private final BooleanSupplier mFlagFunction;
// Whether the flag state should be affected by developer option.
private final boolean mShouldOverrideByDevOption;
@@ -100,7 +127,9 @@ public enum DesktopModeFlags {
// be refreshed only on reboots as overridden state is expected to take effect on reboots.
private static ToggleOverride sCachedToggleOverride;
- DesktopModeFlags(Supplier<Boolean> flagFunction, boolean shouldOverrideByDevOption) {
+ public static final String SYSTEM_PROPERTY_NAME = "persist.wm.debug.desktop_experience_devopts";
+
+ DesktopModeFlags(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) {
this.mFlagFunction = flagFunction;
this.mShouldOverrideByDevOption = shouldOverrideByDevOption;
}
@@ -110,24 +139,42 @@ public enum DesktopModeFlags {
* overrides.
*/
public boolean isTrue() {
- Application application = ActivityThread.currentApplication();
- if (!Flags.showDesktopWindowingDevOption()
- || !mShouldOverrideByDevOption
- || application == null) {
- return mFlagFunction.get();
- } else {
+ return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
+ }
+
+ private static boolean isFlagTrue(BooleanSupplier flagFunction,
+ boolean shouldOverrideByDevOption) {
+ if (!shouldOverrideByDevOption) return flagFunction.getAsBoolean();
+ if (Flags.showDesktopExperienceDevOption()) {
+ return switch (getToggleOverride(null)) {
+ case OVERRIDE_UNSET, OVERRIDE_OFF -> flagFunction.getAsBoolean();
+ case OVERRIDE_ON -> true;
+ };
+ }
+ if (Flags.showDesktopWindowingDevOption()) {
+ Application application = ActivityThread.currentApplication();
+ if (application == null) {
+ Log.w(TAG, "Could not get the current application.");
+ return flagFunction.getAsBoolean();
+ }
+ ContentResolver contentResolver = application.getContentResolver();
+ if (contentResolver == null) {
+ Log.w(TAG, "Could not get the content resolver for the application.");
+ return flagFunction.getAsBoolean();
+ }
boolean shouldToggleBeEnabledByDefault = Flags.enableDesktopWindowingMode();
- return switch (getToggleOverride(application.getContentResolver())) {
- case OVERRIDE_UNSET -> mFlagFunction.get();
+ return switch (getToggleOverride(contentResolver)) {
+ case OVERRIDE_UNSET -> flagFunction.getAsBoolean();
// When toggle override matches its default state, don't override flags. This
// helps users reset their feature overrides.
- case OVERRIDE_OFF -> !shouldToggleBeEnabledByDefault && mFlagFunction.get();
- case OVERRIDE_ON -> shouldToggleBeEnabledByDefault ? mFlagFunction.get() : true;
+ case OVERRIDE_OFF -> !shouldToggleBeEnabledByDefault && flagFunction.getAsBoolean();
+ case OVERRIDE_ON -> !shouldToggleBeEnabledByDefault || flagFunction.getAsBoolean();
};
}
+ return flagFunction.getAsBoolean();
}
- private ToggleOverride getToggleOverride(ContentResolver contentResolver) {
+ private static ToggleOverride getToggleOverride(@Nullable ContentResolver contentResolver) {
// If cached, return it
if (sCachedToggleOverride != null) {
return sCachedToggleOverride;
@@ -143,12 +190,21 @@ public enum DesktopModeFlags {
/**
* Returns {@link ToggleOverride} from Settings.Global set by toggle.
*/
- private ToggleOverride getToggleOverrideFromSystem(ContentResolver contentResolver) {
- int settingValue = Settings.Global.getInt(
- contentResolver,
- Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
- ToggleOverride.OVERRIDE_UNSET.getSetting()
- );
+ private static ToggleOverride getToggleOverrideFromSystem(
+ @Nullable ContentResolver contentResolver) {
+ int settingValue;
+ if (Flags.showDesktopExperienceDevOption()) {
+ settingValue = SystemProperties.getInt(
+ SYSTEM_PROPERTY_NAME,
+ ToggleOverride.OVERRIDE_UNSET.getSetting()
+ );
+ } else {
+ settingValue = Settings.Global.getInt(
+ contentResolver,
+ Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
+ ToggleOverride.OVERRIDE_UNSET.getSetting()
+ );
+ }
return ToggleOverride.fromSetting(settingValue, ToggleOverride.OVERRIDE_UNSET);
}
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index b4e7675402b9..73ebcdd8a07b 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -555,4 +555,11 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
-} \ No newline at end of file
+}
+
+flag {
+ name: "show_desktop_experience_dev_option"
+ namespace: "lse_desktop_experience"
+ description: "Replace the freeform windowing dev options with a desktop experience one."
+ bug: "389092752"
+}
diff --git a/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java
index e0a77d2be724..1f9df3cc842a 100644
--- a/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java
@@ -28,6 +28,7 @@ import com.android.internal.protolog.common.IProtoLogGroup;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.util.ArrayList;
public class ProcessedPerfettoProtoLogImpl extends PerfettoProtoLogImpl {
@@ -161,15 +162,39 @@ public class ProcessedPerfettoProtoLogImpl extends PerfettoProtoLogImpl {
messageString = message.getMessage(mViewerConfigReader);
if (messageString == null) {
- throw new RuntimeException("Failed to decode message for logcat. "
- + "Message hash (" + message.getMessageHash() + ") either not available in "
- + "viewerConfig file (" + mViewerConfigFilePath + ") or "
- + "not loaded into memory from file before decoding.");
+ // Either we failed to load the config for this log message from the viewer config file
+ // into memory, or the message hash is simply not available in the viewer config file.
+ // We want to confirm that the message hash is not available in the viewer config file
+ // before throwing an exception.
+ throw new RuntimeException(getReasonForFailureToGetMessageString(message));
}
return messageString;
}
+ private String getReasonForFailureToGetMessageString(Message message) {
+ if (message.getMessageHash() == null) {
+ return "Trying to get message from null message hash";
+ }
+
+ try {
+ if (mViewerConfigReader.messageHashIsAvailableInFile(message.getMessageHash())) {
+ return "Failed to decode message for logcat logging. "
+ + "Message hash (" + message.getMessageHash() + ") is not available in "
+ + "viewerConfig file (" + mViewerConfigFilePath + "). This might be due "
+ + "to the viewer config file and the executing code being out of sync.";
+ } else {
+ return "Failed to decode message for logcat. "
+ + "Message hash (" + message.getMessageHash() + ") was available in the "
+ + "viewerConfig file (" + mViewerConfigFilePath + ") but wasn't loaded "
+ + "into memory from file before decoding! This is likely a bug.";
+ }
+ } catch (IOException e) {
+ return "Failed to get string message to log but could not identify the root cause due "
+ + "to an IO error in reading the viewer config file.";
+ }
+ }
+
private void loadLogcatGroupsViewerConfig(@NonNull IProtoLogGroup[] protoLogGroups) {
final var groupsLoggingToLogcat = new ArrayList<String>();
for (IProtoLogGroup protoLogGroup : protoLogGroups) {
diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
index 524f64225084..f77179949fbf 100644
--- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
+++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
@@ -100,6 +100,36 @@ public class ProtoLogViewerConfigReader {
}
}
+ /**
+ * Return whether or not the viewer config file contains a message with the specified hash.
+ * @param messageHash The hash message we are looking for in the viewer config file
+ * @return True iff the message with message hash is contained in the viewer config.
+ * @throws IOException if there was an issue reading the viewer config file.
+ */
+ public boolean messageHashIsAvailableInFile(long messageHash)
+ throws IOException {
+ try (var pisWrapper = mViewerConfigInputStreamProvider.getInputStream()) {
+ final var pis = pisWrapper.get();
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (pis.getFieldNumber() == (int) MESSAGES) {
+ final long inMessageToken = pis.start(MESSAGES);
+
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (pis.getFieldNumber() == (int) MESSAGE_ID) {
+ if (pis.readLong(MESSAGE_ID) == messageHash) {
+ return true;
+ }
+ }
+ }
+
+ pis.end(inMessageToken);
+ }
+ }
+ }
+
+ return false;
+ }
+
@NonNull
private Map<Long, String> loadViewerConfigMappingForGroup(@NonNull String group)
throws IOException {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 74707703f5f2..3f9650773211 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1055,7 +1055,7 @@ public class LockPatternUtils {
}
final int patternSize = pattern.size();
- byte[] res = new byte[patternSize];
+ byte[] res = newNonMovableByteArray(patternSize);
for (int i = 0; i < patternSize; i++) {
LockPatternView.Cell cell = pattern.get(i);
res[i] = (byte) (cell.getRow() * 3 + cell.getColumn() + '1');
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
index 92ce990c67df..2a12c986ab04 100644
--- a/core/java/com/android/internal/widget/LockscreenCredential.java
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -81,9 +81,9 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
/**
* Private constructor, use static builder methods instead.
*
- * <p> Builder methods should create a private copy of the credential bytes and pass in here.
- * LockscreenCredential will only store the reference internally without copying. This is to
- * minimize the number of extra copies introduced.
+ * <p> Builder methods should create a private copy of the credential bytes using a non-movable
+ * array and pass it in here. LockscreenCredential will only store the reference internally
+ * without copying. This is to minimize the number of extra copies introduced.
*/
private LockscreenCredential(int type, byte[] credential, boolean hasInvalidChars) {
Objects.requireNonNull(credential);
@@ -141,7 +141,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
*/
public static LockscreenCredential createUnifiedProfilePassword(@NonNull byte[] password) {
return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD,
- Arrays.copyOf(password, password.length), /* hasInvalidChars= */ false);
+ copyOfArrayNonMovable(password), /* hasInvalidChars= */ false);
}
/**
@@ -237,7 +237,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
/** Create a copy of the credential */
public LockscreenCredential duplicate() {
return new LockscreenCredential(mType,
- mCredential != null ? Arrays.copyOf(mCredential, mCredential.length) : null,
+ mCredential != null ? copyOfArrayNonMovable(mCredential) : null,
mHasInvalidChars);
}
@@ -252,6 +252,15 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
}
/**
+ * Copies the given array into a new non-movable array.
+ */
+ private static byte[] copyOfArrayNonMovable(byte[] array) {
+ byte[] copy = LockPatternUtils.newNonMovableByteArray(array.length);
+ System.arraycopy(array, 0, copy, 0, array.length);
+ return copy;
+ }
+
+ /**
* Checks whether the credential meets basic requirements for setting it as a new credential.
*
* This is redundant if {@link android.app.admin.PasswordMetrics#validateCredential()}, which
@@ -440,7 +449,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
* @return A byte array representing the input
*/
private static byte[] charsToBytesTruncating(CharSequence chars) {
- byte[] bytes = new byte[chars.length()];
+ byte[] bytes = LockPatternUtils.newNonMovableByteArray(chars.length());
for (int i = 0; i < chars.length(); i++) {
bytes[i] = (byte) chars.charAt(i);
}
diff --git a/core/res/res/color-night/surface_effect_0_color.xml b/core/res/res/color-night/surface_effect_0_color.xml
new file mode 100644
index 000000000000..f71ed46cc014
--- /dev/null
+++ b/core/res/res/color-night/surface_effect_0_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_accent1_800"
+ android:alpha="0.5"/>
+</selector>
diff --git a/core/res/res/color-night/surface_effect_1_color.xml b/core/res/res/color-night/surface_effect_1_color.xml
new file mode 100644
index 000000000000..80b95ae31d1c
--- /dev/null
+++ b/core/res/res/color-night/surface_effect_1_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500"
+ android:lStar="6" android:alpha="0.54"/>
+</selector>
diff --git a/core/res/res/color/surface_effect_0_color.xml b/core/res/res/color/surface_effect_0_color.xml
new file mode 100644
index 000000000000..b6a607c6ee5e
--- /dev/null
+++ b/core/res/res/color/surface_effect_0_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_accent1_100"
+ android:alpha="0.5"/>
+</selector>
diff --git a/core/res/res/color/surface_effect_1_color.xml b/core/res/res/color/surface_effect_1_color.xml
new file mode 100644
index 000000000000..332130ae6f91
--- /dev/null
+++ b/core/res/res/color/surface_effect_1_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500"
+ android:lStar="98" android:alpha="0.54"/>
+</selector>
diff --git a/core/res/res/drawable/chooser_row_layer_list.xml b/core/res/res/drawable/chooser_row_layer_list.xml
index f5ba1e9d633b..3361fccb70a0 100644
--- a/core/res/res/drawable/chooser_row_layer_list.xml
+++ b/core/res/res/drawable/chooser_row_layer_list.xml
@@ -19,7 +19,7 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
- <solid android:color="?android:attr/colorAccentSecondary"/>
+ <solid android:color="@color/materialColorSecondary"/>
<size android:width="128dp" android:height="2dp"/>
<corners android:radius="2dp" />
</shape>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 397e9a3ef5b9..e8e0f48a966e 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -169,7 +169,7 @@
<string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"درحال‌حاضر تماس‌ها، پیام‌ها، و داده‌ها هنگام استفاده از سیم‌کارت <xliff:g id="NETWORK_NAME">%1$s</xliff:g> آسیب‌پذیرتر هستند.\n\nوقتی اتصال شما دوباره رمزگذاری شود، اعلان دیگری دریافت خواهید کرد."</string>
<string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"تنظیمات امنیت شبکه تلفن همراه"</string>
<string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"بیشتر بدانید"</string>
- <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"متوجه‌ام"</string>
+ <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"متوجهم"</string>
<string name="fcComplete" msgid="1080909484660507044">"کد ویژگی کامل شد."</string>
<string name="fcError" msgid="5325116502080221346">"مشکل در اتصال یا کد ویژگی نامعتبر."</string>
<string name="httpErrorOk" msgid="6206751415788256357">"تأیید"</string>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index d3f998fb70cf..7b9b2d666292 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -47,6 +47,10 @@
<color name="language_picker_item_selected_bg">#92B3F2</color>
<color name="language_picker_item_selected_stroke">#185ABC</color>
+ <!-- Color for various surfaces related to system-wide blur -->
+ <color name="surface_effect_0">@color/surface_effect_0_color</color>
+ <color name="surface_effect_1">@color/surface_effect_1_color</color>
+
<!-- Color for side fps toast dark theme-->
<color name="side_fps_toast_background">#2E3132</color>
<color name="side_fps_text_color">#EFF1F2</color>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 13dd4a35564c..864daf9d2072 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -582,6 +582,10 @@
<color name="side_fps_text_color">#191C1D</color>
<color name="side_fps_button_color">#00677E</color>
+ <!-- Color for various surfaces related to system-wide blur -->
+ <color name="surface_effect_0">@color/surface_effect_0_color</color>
+ <color name="surface_effect_1">@color/surface_effect_1_color</color>
+
<!-- Color for system bars -->
<color name="navigation_bar_compatible">@android:color/black</color>
<!-- This uses non-regular transparent intentionally. It is used to tell if the transparent
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 18e12e300503..586cafdd2b57 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2009,6 +2009,10 @@
<!-- Component name of the built in wallpaper used to display bitmap wallpapers. This must not be null. -->
<string name="image_wallpaper_component" translatable="false">com.android.systemui/com.android.systemui.wallpapers.ImageWallpaper</string>
+ <!-- Component name of the built in wallpaper that is used when the user-selected wallpaper is
+ incompatible with the display's resolution or aspect ratio. -->
+ <string name="fallback_wallpaper_component" translatable="false">com.android.systemui/com.android.systemui.wallpapers.GradientColorWallpaper</string>
+
<!-- True if WallpaperService is enabled -->
<bool name="config_enableWallpaperService">true</bool>
@@ -2889,7 +2893,7 @@
<bool name="config_dozeAlwaysOnEnabled">true</bool>
<!-- If AOD can show an ambient version of the wallpaper -->
- <bool name="config_dozeSupportsAodWallpaper">true</bool>
+ <bool name="config_dozeSupportsAodWallpaper">false</bool>
<!-- Whether the display blanks itself when transitioning from a doze to a non-doze state -->
<bool name="config_displayBlanksAfterDoze">false</bool>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 7baaa6d590f2..a0c4c13a8702 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -137,6 +137,14 @@
<public name="wantsRoleHolderPriority"/>
<!-- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
<public name="minSdkVersionFull"/>
+ <!-- @hide Only for device overlay to use this. -->
+ <public name="pointerIconVectorFill"/>
+ <!-- @hide Only for device overlay to use this. -->
+ <public name="pointerIconVectorFillInverse"/>
+ <!-- @hide Only for device overlay to use this. -->
+ <public name="pointerIconVectorStroke"/>
+ <!-- @hide Only for device overlay to use this. -->
+ <public name="pointerIconVectorStrokeInverse"/>
</staging-public-group>
<staging-public-group type="id" first-id="0x01b60000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6890a4156b75..772a7413a4a7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -532,6 +532,8 @@
<java-symbol type="bool" name="config_enableUdcSysfsUsbStateUpdate"/>
<java-symbol type="bool" name="config_enableSearchTileHideIllustrationInPrivateSpace"/>
+ <java-symbol type="color" name="surface_effect_0" />
+ <java-symbol type="color" name="surface_effect_1" />
<java-symbol type="color" name="tab_indicator_text_v4" />
<java-symbol type="dimen" name="accessibility_touch_slop" />
@@ -2268,6 +2270,7 @@
<java-symbol type="string" name="heavy_weight_notification" />
<java-symbol type="string" name="heavy_weight_notification_detail" />
<java-symbol type="string" name="image_wallpaper_component" />
+ <java-symbol type="string" name="fallback_wallpaper_component" />
<java-symbol type="string" name="input_method_binding_label" />
<java-symbol type="string" name="input_method_ime_switch_long_click_action_desc" />
<java-symbol type="string" name="launch_warning_original" />
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index c40137f1bd34..cef6970ba25a 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -1054,7 +1054,8 @@ public class ViewRootImplTest {
ViewRootImpl viewRootImpl = mView.getViewRootImpl();
sInstrumentation.runOnMainSync(() -> {
mView.invalidate();
- viewRootImpl.notifyInsetsAnimationRunningStateChanged(true);
+ viewRootImpl.notifyInsetsAnimationRunningStateChanged(true, 0 /* animationType */,
+ 0 /* insetsTypes */ /* areOtherAnimationsRunning */);
mView.invalidate();
});
sInstrumentation.waitForIdleSync();
diff --git a/core/tests/coretests/src/android/window/DesktopModeFlagsTest.java b/core/tests/coretests/src/android/window/DesktopModeFlagsTest.java
index b28e2b04b342..49927be65ae5 100644
--- a/core/tests/coretests/src/android/window/DesktopModeFlagsTest.java
+++ b/core/tests/coretests/src/android/window/DesktopModeFlagsTest.java
@@ -21,33 +21,42 @@ import static android.window.DesktopModeFlags.ToggleOverride.OVERRIDE_OFF;
import static android.window.DesktopModeFlags.ToggleOverride.OVERRIDE_ON;
import static android.window.DesktopModeFlags.ToggleOverride.OVERRIDE_UNSET;
import static android.window.DesktopModeFlags.ToggleOverride.fromSetting;
-import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE;
-import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS;
+import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_EXPERIENCE_DEV_OPTION;
import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeTrue;
+
import android.content.ContentResolver;
import android.content.Context;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
+import android.support.test.uiautomator.UiDevice;
+import android.window.DesktopModeFlags.DesktopModeFlag;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.window.flags.Flags;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.lang.reflect.Field;
+import java.util.List;
/**
* Test class for {@link android.window.DesktopModeFlags}
@@ -57,21 +66,39 @@ import java.lang.reflect.Field;
*/
@SmallTest
@Presubmit
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
public class DesktopModeFlagsTest {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
+ FLAG_SHOW_DESKTOP_EXPERIENCE_DEV_OPTION);
+ }
+
@Rule
- public SetFlagsRule setFlagsRule = new SetFlagsRule();
+ public SetFlagsRule mSetFlagsRule;
+ private UiDevice mUiDevice;
private Context mContext;
+ private boolean mLocalFlagValue = false;
+ private final DesktopModeFlag mOverriddenLocalFlag = new DesktopModeFlag(
+ () -> mLocalFlagValue, true);
+ private final DesktopModeFlag mNotOverriddenLocalFlag = new DesktopModeFlag(
+ () -> mLocalFlagValue, false);
private static final int OVERRIDE_OFF_SETTING = 0;
private static final int OVERRIDE_ON_SETTING = 1;
private static final int OVERRIDE_UNSET_SETTING = -1;
+ public DesktopModeFlagsTest(FlagsParameterization flags) {
+ mSetFlagsRule = new SetFlagsRule(flags);
+ }
+
@Before
- public void setUp() {
+ public void setUp() throws Exception {
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ setOverride(null);
}
@After
@@ -80,26 +107,35 @@ public class DesktopModeFlagsTest {
}
@Test
- @DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_devOptionFlagDisabled_overrideOff_featureFlagOn_returnsTrue() {
+ public void isTrue_overrideOff_featureFlagOn() throws Exception {
setOverride(OVERRIDE_OFF_SETTING);
- // In absence of dev options, follow flag
- assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
+
+ if (showDesktopWindowingDevOpts()) {
+ // DW Dev Opts turns off flags when ON
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
+ } else {
+ // DE Dev Opts doesn't turn flags OFF
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
+ }
}
@Test
- @DisableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isTrue_devOptionFlagDisabled_overrideOn_featureFlagOff_returnsFalse() {
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_overrideOn_featureFlagOff() throws Exception {
setOverride(OVERRIDE_ON_SETTING);
- assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
+ if (showAnyDevOpts()) {
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
+ } else {
+ assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
+ }
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isTrue_overrideUnset_featureFlagOn_returnsTrue() {
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_overrideUnset_featureFlagOn() throws Exception {
setOverride(OVERRIDE_UNSET_SETTING);
// For overridableFlag, for unset overrides, follow flag
@@ -107,9 +143,8 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_overrideUnset_featureFlagOff_returnsFalse() {
+ public void isTrue_overrideUnset_featureFlagOff() throws Exception {
setOverride(OVERRIDE_UNSET_SETTING);
// For overridableFlag, for unset overrides, follow flag
@@ -117,8 +152,8 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isTrue_noOverride_featureFlagOn_returnsTrue() {
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_noOverride_featureFlagOn_returnsTrue() throws Exception {
setOverride(null);
// For overridableFlag, in absence of overrides, follow flag
@@ -126,9 +161,8 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_noOverride_featureFlagOff_returnsFalse() {
+ public void isTrue_noOverride_featureFlagOff_returnsFalse() throws Exception {
setOverride(null);
// For overridableFlag, in absence of overrides, follow flag
@@ -136,8 +170,8 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isTrue_unrecognizableOverride_featureFlagOn_returnsTrue() {
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_unrecognizableOverride_featureFlagOn_returnsTrue() throws Exception {
setOverride(-2);
// For overridableFlag, for unrecognized overrides, follow flag
@@ -145,9 +179,8 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_unrecognizableOverride_featureFlagOff_returnsFalse() {
+ public void isTrue_unrecognizableOverride_featureFlagOff_returnsFalse() throws Exception {
setOverride(-2);
// For overridableFlag, for unrecognizable overrides, follow flag
@@ -155,27 +188,10 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isTrue_overrideOff_featureFlagOn_returnsFalse() {
- setOverride(OVERRIDE_OFF_SETTING);
-
- // For overridableFlag, follow override if they exist
- assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
- }
-
- @Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_overrideOn_featureFlagOff_returnsTrue() {
- setOverride(OVERRIDE_ON_SETTING);
-
- // For overridableFlag, follow override if they exist
- assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
- }
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_overrideOffThenOn_featureFlagOn_returnsFalseAndFalse() throws Exception {
+ assumeTrue(showDesktopWindowingDevOpts());
- @Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isTrue_overrideOffThenOn_featureFlagOn_returnsFalseAndFalse() {
setOverride(OVERRIDE_OFF_SETTING);
// For overridableFlag, follow override if they exist
@@ -188,9 +204,9 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_overrideOnThenOff_featureFlagOff_returnsTrueAndTrue() {
+ public void isTrue_overrideOnThenOff_featureFlagOff_returnsTrueAndTrue() throws Exception {
+ assumeTrue(showAnyDevOpts());
setOverride(OVERRIDE_ON_SETTING);
// For overridableFlag, follow override if they exist
@@ -203,146 +219,144 @@ public class DesktopModeFlagsTest {
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS})
- public void isTrue_dwFlagOn_overrideUnset_featureFlagOn_returnsTrue() {
+ @EnableFlags({FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isTrue_dwFlagOn_overrideUnset_featureFlagOn() throws Exception {
+ mLocalFlagValue = true;
setOverride(OVERRIDE_UNSET_SETTING);
// For unset overrides, follow flag
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS)
- public void isTrue_dwFlagOn_overrideUnset_featureFlagOff_returnsFalse() {
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_dwFlagOn_overrideUnset_featureFlagOff() throws Exception {
+ mLocalFlagValue = false;
setOverride(OVERRIDE_UNSET_SETTING);
// For unset overrides, follow flag
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
}
@Test
- @EnableFlags({
- FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
- FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
- public void isTrue_dwFlagOn_overrideOn_featureFlagOn_returnsTrue() {
+ @EnableFlags({FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isTrue_dwFlagOn_overrideOn_featureFlagOn() throws Exception {
+ mLocalFlagValue = true;
setOverride(OVERRIDE_ON_SETTING);
// When toggle override matches its default state (dw flag), don't override flags
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS)
- public void isTrue_dwFlagOn_overrideOn_featureFlagOff_returnsFalse() {
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_dwFlagOn_overrideOn_featureFlagOff() throws Exception {
+ mLocalFlagValue = false;
setOverride(OVERRIDE_ON_SETTING);
- // When toggle override matches its default state (dw flag), don't override flags
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+ if (showDesktopExperienceDevOpts()) {
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ } else {
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ }
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
}
@Test
- @EnableFlags({
- FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
- FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
- public void isTrue_dwFlagOn_overrideOff_featureFlagOn_returnsTrue() {
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_dwFlagOn_overrideOff_featureFlagOn() throws Exception {
+ mLocalFlagValue = true;
setOverride(OVERRIDE_OFF_SETTING);
- // Follow override if they exist, and is not equal to default toggle state (dw flag)
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+ if (showDesktopWindowingDevOpts()) {
+ // Follow override if they exist, and is not equal to default toggle state (dw flag)
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ } else {
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ }
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
}
@Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS)
- public void isTrue_dwFlagOn_overrideOff_featureFlagOff_returnsFalse() {
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_dwFlagOn_overrideOff_featureFlagOff_returnsFalse() throws Exception {
+ mLocalFlagValue = false;
setOverride(OVERRIDE_OFF_SETTING);
// Follow override if they exist, and is not equal to default toggle state (dw flag)
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
}
@Test
- @EnableFlags({
- FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_dwFlagOff_overrideUnset_featureFlagOn_returnsTrue() {
+ public void isTrue_dwFlagOff_overrideUnset_featureFlagOn_returnsTrue() throws Exception {
+ mLocalFlagValue = true;
setOverride(OVERRIDE_UNSET_SETTING);
// For unset overrides, follow flag
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
- @DisableFlags({
- FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
- public void isTrue_dwFlagOff_overrideUnset_featureFlagOff_returnsFalse() {
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_dwFlagOff_overrideUnset_featureFlagOff_returnsFalse() throws Exception {
+ mLocalFlagValue = false;
setOverride(OVERRIDE_UNSET_SETTING);
// For unset overrides, follow flag
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
}
@Test
- @EnableFlags({
- FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_dwFlagOff_overrideOn_featureFlagOn_returnsTrue() {
+ public void isTrue_dwFlagOff_overrideOn_featureFlagOn_returnsTrue() throws Exception {
+ mLocalFlagValue = true;
setOverride(OVERRIDE_ON_SETTING);
// Follow override if they exist, and is not equal to default toggle state (dw flag)
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
- @DisableFlags({
- FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
- public void isTrue_dwFlagOff_overrideOn_featureFlagOff_returnFalse() {
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_dwFlagOff_overrideOn_featureFlagOff() throws Exception {
+ mLocalFlagValue = false;
setOverride(OVERRIDE_ON_SETTING);
- // Follow override if they exist, and is not equal to default toggle state (dw flag)
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+ if (showAnyDevOpts()) {
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ } else {
+ // Follow override if they exist, and is not equal to default toggle state (dw flag)
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ }
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
}
@Test
- @EnableFlags({
- FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isTrue_dwFlagOff_overrideOff_featureFlagOn_returnsTrue() {
+ public void isTrue_dwFlagOff_overrideOff_featureFlagOn_returnsTrue() throws Exception {
+ mLocalFlagValue = true;
setOverride(OVERRIDE_OFF_SETTING);
// When toggle override matches its default state (dw flag), don't override flags
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
- @DisableFlags({
- FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
- })
- public void isTrue_dwFlagOff_overrideOff_featureFlagOff_returnsFalse() {
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isTrue_dwFlagOff_overrideOff_featureFlagOff_returnsFalse() throws Exception {
+ mLocalFlagValue = false;
setOverride(OVERRIDE_OFF_SETTING);
- // When toggle override matches its default state (dw flag), don't override flags
- assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
}
@Test
@@ -365,7 +379,9 @@ public class DesktopModeFlagsTest {
assertThat(OVERRIDE_UNSET.getSetting()).isEqualTo(-1);
}
- private void setOverride(Integer setting) {
+ private void setOverride(Integer setting) throws Exception {
+ setSysProp(setting);
+
ContentResolver contentResolver = mContext.getContentResolver();
String key = Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES;
@@ -376,11 +392,35 @@ public class DesktopModeFlagsTest {
}
}
+ private void setSysProp(Integer value) throws Exception {
+ if (value == null) {
+ resetSysProp();
+ } else {
+ mUiDevice.executeShellCommand(
+ "setprop " + DesktopModeFlags.SYSTEM_PROPERTY_NAME + " " + value);
+ }
+ }
+
+ private void resetSysProp() throws Exception {
+ mUiDevice.executeShellCommand("setprop " + DesktopModeFlags.SYSTEM_PROPERTY_NAME + " ''");
+ }
+
private void resetCache() throws Exception {
Field cachedToggleOverride = DesktopModeFlags.class.getDeclaredField(
"sCachedToggleOverride");
cachedToggleOverride.setAccessible(true);
cachedToggleOverride.set(null, null);
- setOverride(OVERRIDE_UNSET_SETTING);
+ }
+
+ private boolean showDesktopWindowingDevOpts() {
+ return Flags.showDesktopWindowingDevOption() && !Flags.showDesktopExperienceDevOption();
+ }
+
+ private boolean showDesktopExperienceDevOpts() {
+ return Flags.showDesktopExperienceDevOption();
+ }
+
+ private boolean showAnyDevOpts() {
+ return Flags.showDesktopWindowingDevOption() || Flags.showDesktopExperienceDevOption();
}
}
diff --git a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
index d898d222b8de..36c73e2e979e 100644
--- a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
+++ b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
@@ -187,6 +187,9 @@ public class InstallOverlayTests extends BaseHostJUnit4Test {
shell("cmd overlay list " + APP_OVERLAY_PACKAGE_NAME).trim());
assertEquals("STATE_ENABLED",
shell("cmd overlay dump state " + APP_OVERLAY_PACKAGE_NAME).trim());
+
+ assertEquals("STATE_ENABLED",
+ shell("cmd overlay dump --user current state " + APP_OVERLAY_PACKAGE_NAME).trim());
}
private void delay() {
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 45952ea75b6f..3eadf3b94515 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -95,5 +95,6 @@
<permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" />
<permission name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW"/>
<permission name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
+ <permission name="android.permission.SET_UNRESTRICTED_GESTURE_EXCLUSION" />
</privapp-permissions>
</permissions>
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index b332cf0d751f..3d4dccf095f5 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2198,10 +2198,12 @@ public class Paint {
* is configured as {@code 'wght' 500, 'ital' 1}, and if the override is specified as
* {@code 'wght' 700, `wdth` 150}, then the effective font variation setting is
* {@code `wght' 700, 'ital' 1, 'wdth' 150}. The `wght` value is updated by override, 'ital'
- * value is preserved because no overrides, and `wdth` value is added by override.
+ * value is preserved because no overrides, and `wdth` value is added by override. If the font
+ * variation override is empty or null, nothing overrides and original font variation settings
+ * assigned to the font instance is used as it is.
*
- * @param fontVariationOverride font variation settings. You can pass null or empty string as
- * no variation settings.
+ * @param fontVariationOverride font variation override. You can pass null or empty string for
+ * clearing font variation override.
*
* @return true if the provided font variation settings is valid. Otherwise returns false.
*
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 d0d1721115cb..1bcb0bb91515 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -18,6 +18,7 @@ package androidx.window.extensions.embedding;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -3154,15 +3155,22 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
final WindowContainerTransaction wct = transactionRecord.getTransaction();
final TaskFragmentContainer launchedInTaskFragment;
if (launchingActivity != null) {
- final int taskId = getTaskId(launchingActivity);
final String overlayTag = options.getString(KEY_OVERLAY_TAG);
if (Flags.activityEmbeddingOverlayPresentationFlag()
&& overlayTag != null) {
launchedInTaskFragment = createOrUpdateOverlayTaskFragmentIfNeeded(wct,
options, intent, launchingActivity);
} else {
- launchedInTaskFragment = resolveStartActivityIntent(wct, taskId, intent,
- launchingActivity);
+ final int taskId = getTaskId(launchingActivity);
+ if (taskId != INVALID_TASK_ID) {
+ launchedInTaskFragment = resolveStartActivityIntent(wct, taskId, intent,
+ launchingActivity);
+ } else {
+ // We cannot get a valid task id of launchingActivity so we fall back to
+ // treat it as a non-Activity context.
+ launchedInTaskFragment =
+ resolveStartActivityIntentFromNonActivityContext(wct, intent);
+ }
}
} else {
launchedInTaskFragment = resolveStartActivityIntentFromNonActivityContext(wct,
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 4a8ab327acc4..065644627393 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -21,13 +21,6 @@ flag {
}
flag {
- name: "enable_left_right_split_in_portrait"
- namespace: "multitasking"
- description: "Enables left/right split in portrait"
- bug: "291018646"
-}
-
-flag {
name: "enable_new_bubble_animations"
namespace: "multitasking"
description: "Enables new animations for expand and collapse for bubbles"
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index eb50ba7c9477..f9a3c355773c 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -79,7 +79,7 @@
<string name="bubbles_user_education_description" msgid="4215862563054175407">"مکالمه‌های جدید به‌صورت نمادهای شناور یا حبابک‌ها نشان داده می‌شوند. برای باز کردن حبابک‌ها تک‌ضرب بزنید. برای جابه‌جایی، آن را بکشید."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"کنترل حبابک‌ها در هرزمانی"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن حبابک‌ها از این برنامه، روی «مدیریت» تک‌ضرب بزنید"</string>
- <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"متوجه‌ام"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"متوجهم"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"هیچ حبابک جدیدی وجود ندارد"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابک‌های اخیر و حبابک‌های ردشده اینجا ظاهر خواهند شد"</string>
<string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"گپ زدن بااستفاده از حبابک"</string>
@@ -103,7 +103,7 @@
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"از چندین برنامه به‌طور هم‌زمان استفاده کنید"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"برای حالت صفحهٔ دونیمه، در برنامه‌ای دیگر بکشید"</string>
<string name="letterbox_education_reposition_text" msgid="4589957299813220661">"برای جابه‌جا کردن برنامه، بیرون از آن دو تک‌ضرب بزنید"</string>
- <string name="letterbox_education_got_it" msgid="4057634570866051177">"متوجه‌ام"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"متوجهم"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"برای اطلاعات بیشتر، گسترده کنید."</string>
<string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"برای نمایش بهتر بازراه‌اندازی شود؟"</string>
<string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"می‌توانید برنامه را بازراه‌اندازی کنید تا بهتر روی صفحه‌نمایش نشان داده شود، اما ممکن است پیشرفت یا تغییرات ذخیره‌نشده را ازدست بدهید"</string>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index 2fed1380b635..1ee71ca78815 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -218,6 +218,13 @@ public class DesktopModeStatus {
return isDeviceEligibleForDesktopMode(context) && Flags.showDesktopWindowingDevOption();
}
+ /**
+ * Return {@code true} if desktop mode dev option should be shown on current device
+ */
+ public static boolean canShowDesktopExperienceDevOption(@NonNull Context context) {
+ return Flags.showDesktopExperienceDevOption();
+ }
+
/** Returns if desktop mode dev option should be enabled if there is no user override. */
public static boolean shouldDevOptionBeEnabledByDefault() {
return Flags.enableDesktopWindowingMode();
@@ -290,7 +297,7 @@ public class DesktopModeStatus {
/**
* Return {@code true} if desktop mode is unrestricted and is supported in the device.
*/
- private static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) {
+ public static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) {
return !enforceDeviceRestrictions() || isDesktopModeSupported(context);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStack.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStack.kt
deleted file mode 100644
index caacdd355996..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStack.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.automotive
-
-import android.app.ActivityManager
-import android.graphics.Rect
-import android.view.SurfaceControl
-
-/**
- * Represents an auto task stack, which is always in multi-window mode.
- *
- * @property id The ID of the task stack.
- * @property displayId The ID of the display the task stack is on.
- * @property leash The surface control leash of the task stack.
- */
-interface AutoTaskStack {
- val id: Int
- val displayId: Int
- var leash: SurfaceControl
-}
-
-/**
- * Data class representing the state of an auto task stack.
- *
- * @property bounds The bounds of the task stack.
- * @property childrenTasksVisible Whether the child tasks of the stack are visible.
- * @property layer The layer of the task stack.
- */
-data class AutoTaskStackState(
- val bounds: Rect = Rect(),
- val childrenTasksVisible: Boolean,
- val layer: Int
-)
-
-/**
- * Data class representing a root task stack.
- *
- * @property id The ID of the root task stack
- * @property displayId The ID of the display the root task stack is on.
- * @property leash The surface control leash of the root task stack.
- * @property rootTaskInfo The running task info of the root task.
- */
-data class RootTaskStack(
- override val id: Int,
- override val displayId: Int,
- override var leash: SurfaceControl,
- var rootTaskInfo: ActivityManager.RunningTaskInfo
-) : AutoTaskStack
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackController.kt
deleted file mode 100644
index 15fedac62af3..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackController.kt
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.automotive
-
-import android.app.PendingIntent
-import android.content.Intent
-import android.os.Bundle
-import android.os.IBinder
-import android.view.SurfaceControl
-import android.window.TransitionInfo
-import android.window.TransitionRequestInfo
-import com.android.wm.shell.shared.annotations.ShellMainThread
-import com.android.wm.shell.transition.Transitions
-import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
-
-/**
- * Delegate interface for handling auto task stack transitions.
- */
-interface AutoTaskStackTransitionHandlerDelegate {
- /**
- * Handles a transition request.
- *
- * @param transition The transition identifier.
- * @param request The transition request information.
- * @return An [AutoTaskStackTransaction] to be applied for the transition, or null if the
- * animation is not handled by this delegate.
- */
- fun handleRequest(
- transition: IBinder, request: TransitionRequestInfo
- ): AutoTaskStackTransaction?
-
- /**
- * See [Transitions.TransitionHandler.startAnimation] for more details.
- *
- * @param changedTaskStacks Contains the states of the task stacks that were changed as a
- * result of this transition. The key is the [AutoTaskStack.id] and the value is the
- * corresponding [AutoTaskStackState].
- */
- fun startAnimation(
- transition: IBinder,
- changedTaskStacks: Map<Int, AutoTaskStackState>,
- info: TransitionInfo,
- startTransaction: SurfaceControl.Transaction,
- finishTransaction: SurfaceControl.Transaction,
- finishCallback: TransitionFinishCallback
- ): Boolean
-
- /**
- * See [Transitions.TransitionHandler.onTransitionConsumed] for more details.
- *
- * @param requestedTaskStacks contains the states of the task stacks that were requested in
- * the transition. The key is the [AutoTaskStack.id] and the value is the corresponding
- * [AutoTaskStackState].
- */
- fun onTransitionConsumed(
- transition: IBinder,
- requestedTaskStacks: Map<Int, AutoTaskStackState>,
- aborted: Boolean, finishTransaction: SurfaceControl.Transaction?
- )
-
- /**
- * See [Transitions.TransitionHandler.mergeAnimation] for more details.
- *
- * @param changedTaskStacks Contains the states of the task stacks that were changed as a
- * result of this transition. The key is the [AutoTaskStack.id] and the value is the
- * corresponding [AutoTaskStackState].
- */
- fun mergeAnimation(
- transition: IBinder,
- changedTaskStacks: Map<Int, AutoTaskStackState>,
- info: TransitionInfo,
- surfaceTransaction: SurfaceControl.Transaction,
- mergeTarget: IBinder,
- finishCallback: TransitionFinishCallback
- )
-}
-
-
-/**
- * Controller for managing auto task stacks.
- */
-interface AutoTaskStackController {
-
- var autoTransitionHandlerDelegate: AutoTaskStackTransitionHandlerDelegate?
- set
-
- /**
- * Map of task stack IDs to their states.
- *
- * This gets updated right before [AutoTaskStackTransitionHandlerDelegate.startAnimation] or
- * [AutoTaskStackTransitionHandlerDelegate.onTransitionConsumed] is called.
- */
- val taskStackStateMap: Map<Int, AutoTaskStackState>
- get
-
- /**
- * Creates a new multi-window root task.
- *
- * A root task stack is placed in the default TDA of the specified display by default.
- * Once the root task is removed, the [AutoTaskStackController] no longer holds a reference to
- * it.
- *
- * @param displayId The ID of the display to create the root task stack on.
- * @param listener The listener for root task stack events.
- */
- @ShellMainThread
- fun createRootTaskStack(displayId: Int, listener: RootTaskStackListener)
-
-
- /**
- * Sets the default root task stack (launch root) on a display. Calling it again with a
- * different [rootTaskStackId] will simply replace the default root task stack on the display.
- *
- * Note: This is helpful for passively routing tasks to a specified container. If a display
- * doesn't have a default root task stack set, all tasks will open in fullscreen and cover
- * the entire default TDA by default.
- *
- * @param displayId The ID of the display.
- * @param rootTaskStackId The ID of the root task stack, or null to clear the default.
- */
- @ShellMainThread
- fun setDefaultRootTaskStackOnDisplay(displayId: Int, rootTaskStackId: Int?)
-
- /**
- * Starts a transaction with the specified [transaction].
- * Returns the transition identifier.
- */
- @ShellMainThread
- fun startTransition(transaction: AutoTaskStackTransaction): IBinder?
-}
-
-internal sealed class TaskStackOperation {
- data class ReparentTask(
- val taskId: Int,
- val parentTaskStackId: Int,
- val onTop: Boolean
- ) : TaskStackOperation()
-
- data class SendPendingIntent(
- val sender: PendingIntent,
- val intent: Intent,
- val options: Bundle?
- ) : TaskStackOperation()
-
- data class SetTaskStackState(
- val taskStackId: Int,
- val state: AutoTaskStackState
- ) : TaskStackOperation()
-}
-
-data class AutoTaskStackTransaction internal constructor(
- internal val operations: MutableList<TaskStackOperation> = mutableListOf()
-) {
- constructor() : this(
- mutableListOf()
- )
-
- /** See [WindowContainerTransaction.reparent] for more details. */
- fun reparentTask(
- taskId: Int,
- parentTaskStackId: Int,
- onTop: Boolean
- ): AutoTaskStackTransaction {
- operations.add(TaskStackOperation.ReparentTask(taskId, parentTaskStackId, onTop))
- return this
- }
-
- /** See [WindowContainerTransaction.sendPendingIntent] for more details. */
- fun sendPendingIntent(
- sender: PendingIntent,
- intent: Intent,
- options: Bundle?
- ): AutoTaskStackTransaction {
- operations.add(TaskStackOperation.SendPendingIntent(sender, intent, options))
- return this
- }
-
- /**
- * Adds a set task stack state operation to the transaction.
- *
- * If an operation with the same task stack ID already exists, it is replaced with the new one.
- *
- * @param taskStackId The ID of the task stack.
- * @param state The new state of the task stack.
- * @return The transaction with the added operation.
- */
- fun setTaskStackState(taskStackId: Int, state: AutoTaskStackState): AutoTaskStackTransaction {
- val existingOperation = operations.find {
- it is TaskStackOperation.SetTaskStackState && it.taskStackId == taskStackId
- }
- if (existingOperation != null) {
- val index = operations.indexOf(existingOperation)
- operations[index] = TaskStackOperation.SetTaskStackState(taskStackId, state)
- } else {
- operations.add(TaskStackOperation.SetTaskStackState(taskStackId, state))
- }
- return this
- }
-
- /**
- * Returns a map of task stack IDs to their states from the set task stack state operations.
- *
- * @return The map of task stack IDs to states.
- */
- fun getTaskStackStates(): Map<Int, AutoTaskStackState> {
- val states = mutableMapOf<Int, AutoTaskStackState>()
- operations.forEach { operation ->
- if (operation is TaskStackOperation.SetTaskStackState) {
- states[operation.taskStackId] = operation.state
- }
- }
- return states
- }
-}
-
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackControllerImpl.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackControllerImpl.kt
deleted file mode 100644
index 8171312762ef..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackControllerImpl.kt
+++ /dev/null
@@ -1,534 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.automotive
-
-import android.app.ActivityManager
-import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT
-import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS
-import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
-import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
-import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
-import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
-import android.os.IBinder
-import android.util.Log
-import android.util.Slog
-import android.view.SurfaceControl
-import android.view.SurfaceControl.Transaction
-import android.view.WindowManager
-import android.view.WindowManager.TRANSIT_CHANGE
-import android.window.TransitionInfo
-import android.window.TransitionRequestInfo
-import android.window.WindowContainerTransaction
-import com.android.wm.shell.Flags.enableAutoTaskStackController
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer
-import com.android.wm.shell.ShellTaskOrganizer
-import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.dagger.WMSingleton
-import com.android.wm.shell.shared.TransitionUtil
-import com.android.wm.shell.shared.annotations.ShellMainThread
-import com.android.wm.shell.sysui.ShellInit
-import com.android.wm.shell.transition.Transitions
-import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
-import javax.inject.Inject
-
-const val TAG = "AutoTaskStackController"
-
-@WMSingleton
-class AutoTaskStackControllerImpl @Inject constructor(
- val taskOrganizer: ShellTaskOrganizer,
- @ShellMainThread private val shellMainThread: ShellExecutor,
- val transitions: Transitions,
- val shellInit: ShellInit,
- val rootTdaOrganizer: RootTaskDisplayAreaOrganizer
-) : AutoTaskStackController, Transitions.TransitionHandler {
- override var autoTransitionHandlerDelegate: AutoTaskStackTransitionHandlerDelegate? = null
- override val taskStackStateMap = mutableMapOf<Int, AutoTaskStackState>()
-
- private val DBG = Log.isLoggable(TAG, Log.DEBUG)
- private val taskStackMap = mutableMapOf<Int, AutoTaskStack>()
- private val pendingTransitions = ArrayList<PendingTransition>()
- private val mTaskStackStateTranslator = TaskStackStateTranslator()
- private val appTasksMap = mutableMapOf<Int, ActivityManager.RunningTaskInfo>()
- private val defaultRootTaskPerDisplay = mutableMapOf<Int, Int>()
-
- init {
- if (!enableAutoTaskStackController()) {
- throw IllegalStateException("Failed to initialize" +
- "AutoTaskStackController as the auto_task_stack_windowing TS flag is disabled.")
- } else {
- shellInit.addInitCallback(this::onInit, this);
- }
- }
-
- fun onInit() {
- transitions.addHandler(this)
- }
-
- /** Translates the [AutoTaskStackState] to relevant WM and surface transactions. */
- inner class TaskStackStateTranslator {
- // TODO(b/384946072): Move to an interface with 2 implementations, one for root task and
- // other for TDA
- fun applyVisibilityAndBounds(
- wct: WindowContainerTransaction,
- taskStack: AutoTaskStack,
- state: AutoTaskStackState
- ) {
- if (taskStack !is RootTaskStack) {
- Slog.e(TAG, "Unsupported task stack, unable to convertToWct")
- return
- }
- wct.setBounds(taskStack.rootTaskInfo.token, state.bounds)
- wct.reorder(taskStack.rootTaskInfo.token, /* onTop= */ state.childrenTasksVisible)
- }
-
- fun reorderLeash(
- taskStack: AutoTaskStack,
- state: AutoTaskStackState,
- transaction: Transaction
- ) {
- if (taskStack !is RootTaskStack) {
- Slog.e(TAG, "Unsupported task stack, unable to reorder leash")
- return
- }
- Slog.d(TAG, "Setting the layer ${state.layer}")
- transaction.setLayer(taskStack.leash, state.layer)
- }
-
- fun restoreLeash(taskStack: AutoTaskStack, transaction: Transaction) {
- if (taskStack !is RootTaskStack) {
- Slog.e(TAG, "Unsupported task stack, unable to restore leash")
- return
- }
-
- val rootTdaInfo = rootTdaOrganizer.getDisplayAreaInfo(taskStack.displayId)
- if (rootTdaInfo == null ||
- rootTdaInfo.featureId != taskStack.rootTaskInfo.displayAreaFeatureId
- ) {
- Slog.e(TAG, "Cannot find the rootTDA for the root task stack ${taskStack.id}")
- return
- }
- if (DBG) {
- Slog.d(TAG, "Reparenting ${taskStack.id} leash to DA ${rootTdaInfo.featureId}")
- }
- transaction.reparent(
- taskStack.leash,
- rootTdaOrganizer.getDisplayAreaLeash(taskStack.displayId)
- )
- }
- }
-
- inner class RootTaskStackListenerAdapter(
- val rootTaskStackListener: RootTaskStackListener,
- ) : ShellTaskOrganizer.TaskListener {
- private var rootTaskStack: RootTaskStack? = null
-
- // TODO(b/384948029): Notify car service for all the children tasks' events
- override fun onTaskAppeared(
- taskInfo: ActivityManager.RunningTaskInfo?,
- leash: SurfaceControl?
- ) {
- if (taskInfo == null) {
- throw IllegalArgumentException("taskInfo can't be null in onTaskAppeared")
- }
- if (leash == null) {
- throw IllegalArgumentException("leash can't be null in onTaskAppeared")
- }
- if (DBG) Slog.d(TAG, "onTaskAppeared = ${taskInfo.taskId}")
-
- if (rootTaskStack == null) {
- val rootTask =
- RootTaskStack(taskInfo.taskId, taskInfo.displayId, leash, taskInfo)
- taskStackMap[rootTask.id] = rootTask
-
- rootTaskStack = rootTask;
- rootTaskStackListener.onRootTaskStackCreated(rootTask);
- return
- }
- appTasksMap[taskInfo.taskId] = taskInfo
- rootTaskStackListener.onTaskAppeared(taskInfo, leash)
- }
-
- override fun onTaskInfoChanged(taskInfo: ActivityManager.RunningTaskInfo?) {
- if (taskInfo == null) {
- throw IllegalArgumentException("taskInfo can't be null in onTaskInfoChanged")
- }
- if (DBG) Slog.d(TAG, "onTaskInfoChanged = ${taskInfo.taskId}")
- var previousRootTaskStackInfo = rootTaskStack ?: run {
- Slog.e(TAG, "Received onTaskInfoChanged, when root task stack is null")
- return@onTaskInfoChanged
- }
- rootTaskStack?.let {
- if (taskInfo.taskId == previousRootTaskStackInfo.id) {
- previousRootTaskStackInfo = previousRootTaskStackInfo.copy(rootTaskInfo = taskInfo)
- taskStackMap[previousRootTaskStackInfo.id] = previousRootTaskStackInfo
- rootTaskStack = previousRootTaskStackInfo;
- rootTaskStackListener.onRootTaskStackInfoChanged(it)
- return
- }
- }
-
- appTasksMap[taskInfo.taskId] = taskInfo
- rootTaskStackListener.onTaskInfoChanged(taskInfo)
- }
-
- override fun onTaskVanished(taskInfo: ActivityManager.RunningTaskInfo?) {
- if (taskInfo == null) {
- throw IllegalArgumentException("taskInfo can't be null in onTaskVanished")
- }
- if (DBG) Slog.d(TAG, "onTaskVanished = ${taskInfo.taskId}")
- var rootTask = rootTaskStack ?: run {
- Slog.e(TAG, "Received onTaskVanished, when root task stack is null")
- return@onTaskVanished
- }
- if (taskInfo.taskId == rootTask.id) {
- rootTask = rootTask.copy(rootTaskInfo = taskInfo)
- rootTaskStack = rootTask
- rootTaskStackListener.onRootTaskStackDestroyed(rootTask)
- taskStackMap.remove(rootTask.id)
- taskStackStateMap.remove(rootTask.id)
- rootTaskStack = null
- return
- }
- appTasksMap.remove(taskInfo.taskId)
- rootTaskStackListener.onTaskVanished(taskInfo)
- }
-
- override fun onBackPressedOnTaskRoot(taskInfo: ActivityManager.RunningTaskInfo?) {
- if (taskInfo == null) {
- throw IllegalArgumentException("taskInfo can't be null in onBackPressedOnTaskRoot")
- }
- super.onBackPressedOnTaskRoot(taskInfo)
- rootTaskStackListener.onBackPressedOnTaskRoot(taskInfo)
- }
- }
-
- override fun createRootTaskStack(
- displayId: Int,
- listener: RootTaskStackListener
- ) {
- if (!enableAutoTaskStackController()) {
- Slog.e(
- TAG, "Failed to create root task stack as the " +
- "auto_task_stack_windowing TS flag is disabled."
- )
- return
- }
- taskOrganizer.createRootTask(
- displayId,
- WINDOWING_MODE_MULTI_WINDOW,
- RootTaskStackListenerAdapter(listener),
- /* removeWithTaskOrganizer= */ true
- )
- }
-
- override fun setDefaultRootTaskStackOnDisplay(displayId: Int, rootTaskStackId: Int?) {
- if (!enableAutoTaskStackController()) {
- Slog.e(
- TAG, "Failed to set default root task stack as the " +
- "auto_task_stack_windowing TS flag is disabled."
- )
- return
- }
- var wct = WindowContainerTransaction()
-
- // Clear the default root task stack if already set
- defaultRootTaskPerDisplay[displayId]?.let { existingDefaultRootTaskStackId ->
- (taskStackMap[existingDefaultRootTaskStackId] as? RootTaskStack)?.let { rootTaskStack ->
- wct.setLaunchRoot(rootTaskStack.rootTaskInfo.token, null, null)
- }
- }
-
- if (rootTaskStackId != null) {
- var taskStack =
- taskStackMap[rootTaskStackId] ?: run { return@setDefaultRootTaskStackOnDisplay }
- if (DBG) Slog.d(TAG, "setting launch root for = ${taskStack.id}")
- if (taskStack !is RootTaskStack) {
- throw IllegalArgumentException(
- "Cannot set a non root task stack as default root task " +
- "stack"
- )
- }
- wct.setLaunchRoot(
- taskStack.rootTaskInfo.token,
- intArrayOf(WINDOWING_MODE_UNDEFINED),
- intArrayOf(
- ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_UNDEFINED, ACTIVITY_TYPE_RECENTS,
-
- // TODO(b/386242708): Figure out if this flag will ever be used for automotive
- // assistant. Based on output, remove it from here and fix the
- // AssistantStackTests accordingly.
- ACTIVITY_TYPE_ASSISTANT
- )
- )
- }
-
- taskOrganizer.applyTransaction(wct)
- }
-
- override fun startTransition(transaction: AutoTaskStackTransaction): IBinder? {
- if (!enableAutoTaskStackController()) {
- Slog.e(
- TAG, "Failed to start transaction as the " +
- "auto_task_stack_windowing TS flag is disabled."
- )
- return null
- }
- if (transaction.operations.isEmpty()) {
- Slog.e(TAG, "Operations empty, no transaction started")
- return null
- }
- if (DBG) Slog.d(TAG, "startTransaction ${transaction.operations}")
-
- var wct = WindowContainerTransaction()
- convertToWct(transaction, wct)
- var pending = PendingTransition(
- TRANSIT_CHANGE,
- wct,
- transaction,
- )
- return startTransitionNow(pending)
- }
-
- override fun handleRequest(
- transition: IBinder,
- request: TransitionRequestInfo
- ): WindowContainerTransaction? {
- if (DBG) {
- Slog.d(TAG, "handle request, id=${request.debugId}, type=${request.type}, " +
- "triggertask = ${request.triggerTask ?: "null"}")
- }
- val ast = autoTransitionHandlerDelegate?.handleRequest(transition, request)
- ?: run { return@handleRequest null }
-
- if (ast.operations.isEmpty()) {
- return null
- }
- var wct = WindowContainerTransaction()
- convertToWct(ast, wct)
-
- pendingTransitions.add(
- PendingTransition(request.type, wct, ast).apply { isClaimed = transition }
- )
- return wct
- }
-
- fun updateTaskStackStates(taskStatStates: Map<Int, AutoTaskStackState>) {
- taskStackStateMap.putAll(taskStatStates)
- }
-
- override fun startAnimation(
- transition: IBinder,
- info: TransitionInfo,
- startTransaction: Transaction,
- finishTransaction: Transaction,
- finishCallback: TransitionFinishCallback
- ): Boolean {
- if (DBG) Slog.d(TAG, " startAnimation, id=${info.debugId} = changes=" + info.changes)
- val pending: PendingTransition? = findPending(transition)
- if (pending != null) {
- pendingTransitions.remove(pending)
- updateTaskStackStates(pending.transaction.getTaskStackStates())
- }
-
- reorderLeashes(startTransaction)
- reorderLeashes(finishTransaction)
-
- for (chg in info.changes) {
- // TODO(b/384946072): handle the da stack similarly. The below implementation only
- // handles the root task stack
-
- val taskInfo = chg.taskInfo ?: continue
- val taskStack = taskStackMap[taskInfo.taskId] ?: continue
-
- // Restore the leashes for the task stacks to ensure correct z-order competition
- if (taskStackMap.containsKey(taskInfo.taskId)) {
- mTaskStackStateTranslator.restoreLeash(
- taskStack,
- startTransaction
- )
- if (TransitionUtil.isOpeningMode(chg.mode)) {
- // Clients can still manipulate the alpha, but this ensures that the default
- // behavior is natural
- startTransaction.setAlpha(chg.leash, 1f)
- }
- continue
- }
- }
-
- val isPlayedByDelegate = autoTransitionHandlerDelegate?.startAnimation(
- transition,
- pending?.transaction?.getTaskStackStates() ?: mapOf(),
- info,
- startTransaction,
- finishTransaction,
- {
- shellMainThread.execute {
- finishCallback.onTransitionFinished(it)
- startNextTransition()
- }
- }
- ) ?: false
-
- if (isPlayedByDelegate) {
- if (DBG) Slog.d(TAG, "${info.debugId} played");
- return true;
- }
-
- // If for an animation which is not played by the delegate, contains a change in a known
- // task stack, it should be leveraged to correct the leashes. So, handle the animation in
- // this case.
- if (info.changes.any { taskStackMap.containsKey(it.taskInfo?.taskId) }) {
- startTransaction.apply()
- finishCallback.onTransitionFinished(null)
- startNextTransition()
- if (DBG) Slog.d(TAG, "${info.debugId} played");
- return true
- }
- return false;
- }
-
- fun convertToWct(ast: AutoTaskStackTransaction, wct: WindowContainerTransaction) {
- ast.operations.forEach { operation ->
- when (operation) {
- is TaskStackOperation.ReparentTask -> {
- val appTask = appTasksMap[operation.taskId]
-
- if (appTask == null) {
- Slog.e(
- TAG, "task with id=$operation.taskId not found, failed to " +
- "reparent."
- )
- return@forEach
- }
- if (!taskStackMap.containsKey(operation.parentTaskStackId)) {
- Slog.e(
- TAG, "task stack with id=${operation.parentTaskStackId} not " +
- "found, failed to reparent"
- )
- return@forEach
- }
- // TODO(b/384946072): Handle a display area stack as well
- wct.reparent(
- appTask.token,
- (taskStackMap[operation.parentTaskStackId] as RootTaskStack)
- .rootTaskInfo.token,
- operation.onTop
- )
- }
-
- is TaskStackOperation.SendPendingIntent -> wct.sendPendingIntent(
- operation.sender,
- operation.intent,
- operation.options
- )
-
- is TaskStackOperation.SetTaskStackState -> {
- taskStackMap[operation.taskStackId]?.let { taskStack ->
- mTaskStackStateTranslator.applyVisibilityAndBounds(
- wct,
- taskStack,
- operation.state
- )
- }
- ?: Slog.w(TAG, "AutoTaskStack with id ${operation.taskStackId} " +
- "not found.")
- }
- }
- }
- }
-
- override fun mergeAnimation(
- transition: IBinder,
- info: TransitionInfo,
- surfaceTransaction: Transaction,
- mergeTarget: IBinder,
- finishCallback: TransitionFinishCallback
- ) {
- val pending: PendingTransition? = findPending(transition)
-
- autoTransitionHandlerDelegate?.mergeAnimation(
- transition,
- pending?.transaction?.getTaskStackStates() ?: mapOf(),
- info,
- surfaceTransaction,
- mergeTarget,
- /* finishCallback = */ {
- shellMainThread.execute {
- finishCallback.onTransitionFinished(it)
- }
- }
- )
- }
-
- override fun onTransitionConsumed(
- transition: IBinder,
- aborted: Boolean,
- finishTransaction: Transaction?
- ) {
- val pending: PendingTransition? = findPending(transition)
- if (pending != null) {
- pendingTransitions.remove(pending)
- updateTaskStackStates(pending.transaction.getTaskStackStates())
- // Still update the surface order because this means wm didn't lead to any change
- if (finishTransaction != null) {
- reorderLeashes(finishTransaction)
- }
- }
- autoTransitionHandlerDelegate?.onTransitionConsumed(
- transition,
- pending?.transaction?.getTaskStackStates() ?: mapOf(),
- aborted,
- finishTransaction
- )
- }
-
- private fun reorderLeashes(transaction: SurfaceControl.Transaction) {
- taskStackStateMap.forEach { (taskId, taskStackState) ->
- taskStackMap[taskId]?.let { taskStack ->
- mTaskStackStateTranslator.reorderLeash(taskStack, taskStackState, transaction)
- } ?: Slog.w(TAG, "Warning: AutoTaskStack with id $taskId not found.")
- }
- }
-
- private fun findPending(claimed: IBinder) = pendingTransitions.find { it.isClaimed == claimed }
-
- private fun startTransitionNow(pending: PendingTransition): IBinder {
- val claimedTransition = transitions.startTransition(pending.mType, pending.wct, this)
- pending.isClaimed = claimedTransition
- pendingTransitions.add(pending)
- return claimedTransition
- }
-
- fun startNextTransition() {
- if (pendingTransitions.isEmpty()) return
- val pending: PendingTransition = pendingTransitions[0]
- if (pending.isClaimed != null) {
- // Wait for this to start animating.
- return
- }
- pending.isClaimed = transitions.startTransition(pending.mType, pending.wct, this)
- }
-
- internal class PendingTransition(
- @field:WindowManager.TransitionType @param:WindowManager.TransitionType val mType: Int,
- val wct: WindowContainerTransaction,
- val transaction: AutoTaskStackTransaction,
- ) {
- var isClaimed: IBinder? = null
- }
-
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/OWNERS
deleted file mode 100644
index 84596b015209..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-# WM shell sub-module automotive owners
-
-winsonc@google.com
-stenning@google.com
-gauravbhola@google.com
-xiangw@google.com \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/RootTaskStackListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/RootTaskStackListener.kt
deleted file mode 100644
index 9d121b144492..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/RootTaskStackListener.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.automotive
-
-import com.android.wm.shell.ShellTaskOrganizer
-
-/**
- * A [TaskListener] which simplifies the interface when used for
- * [ShellTaskOrganizer.createRootTask].
- *
- * [onRootTaskStackCreated], [onRootTaskStackInfoChanged], [onRootTaskStackDestroyed] will be called
- * for the underlying root task.
- * The [onTaskAppeared], [onTaskInfoChanged], [onTaskVanished] are called for the children tasks.
- */
-interface RootTaskStackListener : ShellTaskOrganizer.TaskListener {
- fun onRootTaskStackCreated(rootTaskStack: RootTaskStack)
- fun onRootTaskStackInfoChanged(rootTaskStack: RootTaskStack)
- fun onRootTaskStackDestroyed(rootTaskStack: RootTaskStack)
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index e8e25e20d8d8..e68a98fb7f21 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -28,6 +28,7 @@ import android.annotation.Nullable;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Person;
+import android.app.TaskInfo;
import android.content.Context;
import android.content.Intent;
import android.content.LocusId;
@@ -57,6 +58,7 @@ import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.bubbles.BubbleInfo;
import com.android.wm.shell.shared.bubbles.ParcelableFlyoutMessage;
+import com.android.wm.shell.taskview.TaskView;
import java.io.PrintWriter;
import java.util.List;
@@ -204,6 +206,13 @@ public class Bubble implements BubbleViewProvider {
private Intent mAppIntent;
/**
+ * Set while preparing a transition for animation. Several steps are needed before animation
+ * starts, so this is used to detect and route associated events to the coordinating transition.
+ */
+ @Nullable
+ private BubbleTransitions.BubbleTransition mPreparingTransition;
+
+ /**
* Create a bubble with limited information based on given {@link ShortcutInfo}.
* Note: Currently this is only being used when the bubble is persisted to disk.
*/
@@ -280,6 +289,30 @@ public class Bubble implements BubbleViewProvider {
mShortcutInfo = info;
}
+ private Bubble(
+ TaskInfo task,
+ UserHandle user,
+ @Nullable Icon icon,
+ String key,
+ @ShellMainThread Executor mainExecutor,
+ @ShellBackgroundThread Executor bgExecutor) {
+ mGroupKey = null;
+ mLocusId = null;
+ mFlags = 0;
+ mUser = user;
+ mIcon = icon;
+ mIsAppBubble = true;
+ mKey = key;
+ mShowBubbleUpdateDot = false;
+ mMainExecutor = mainExecutor;
+ mBgExecutor = bgExecutor;
+ mTaskId = task.taskId;
+ mAppIntent = null;
+ mDesiredHeight = Integer.MAX_VALUE;
+ mPackageName = task.baseActivity.getPackageName();
+ }
+
+
/** Creates an app bubble. */
public static Bubble createAppBubble(Intent intent, UserHandle user, @Nullable Icon icon,
@ShellMainThread Executor mainExecutor, @ShellBackgroundThread Executor bgExecutor) {
@@ -291,6 +324,16 @@ public class Bubble implements BubbleViewProvider {
mainExecutor, bgExecutor);
}
+ /** Creates a task bubble. */
+ public static Bubble createTaskBubble(TaskInfo info, UserHandle user, @Nullable Icon icon,
+ @ShellMainThread Executor mainExecutor, @ShellBackgroundThread Executor bgExecutor) {
+ return new Bubble(info,
+ user,
+ icon,
+ getAppBubbleKeyForTask(info),
+ mainExecutor, bgExecutor);
+ }
+
/** Creates a shortcut bubble. */
public static Bubble createShortcutBubble(
ShortcutInfo info,
@@ -316,6 +359,15 @@ public class Bubble implements BubbleViewProvider {
return info.getPackage() + ":" + info.getUserId() + ":" + info.getId();
}
+ /**
+ * Returns the key for an app bubble from an app with package name, {@code packageName} on an
+ * Android user, {@code user}.
+ */
+ public static String getAppBubbleKeyForTask(TaskInfo taskInfo) {
+ Objects.requireNonNull(taskInfo);
+ return KEY_APP_BUBBLE + ":" + taskInfo.taskId;
+ }
+
@VisibleForTesting(visibility = PRIVATE)
public Bubble(@NonNull final BubbleEntry entry,
final Bubbles.BubbleMetadataFlagListener listener,
@@ -469,6 +521,10 @@ public class Bubble implements BubbleViewProvider {
return mBubbleTaskView;
}
+ public TaskView getTaskView() {
+ return mBubbleTaskView.getTaskView();
+ }
+
/**
* @return the ShortcutInfo id if it exists, or the metadata shortcut id otherwise.
*/
@@ -486,6 +542,10 @@ public class Bubble implements BubbleViewProvider {
return (mMetadataShortcutId != null && !mMetadataShortcutId.isEmpty());
}
+ BubbleTransitions.BubbleTransition getPreparingTransition() {
+ return mPreparingTransition;
+ }
+
/**
* Call this to clean up the task for the bubble. Ensure this is always called when done with
* the bubble.
@@ -556,6 +616,13 @@ public class Bubble implements BubbleViewProvider {
}
/**
+ * Sets the current bubble-transition that is coordinating a change in this bubble.
+ */
+ void setPreparingTransition(BubbleTransitions.BubbleTransition transit) {
+ mPreparingTransition = transit;
+ }
+
+ /**
* Sets whether this bubble is considered text changed. This method is purely for
* testing.
*/
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 4f9028e8aaf3..d0f912ac2142 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
@@ -110,6 +110,7 @@ import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
@@ -287,6 +288,8 @@ public class BubbleController implements ConfigurationChangeListener,
/** Used to send updates to the views from {@link #mBubbleDataListener}. */
private BubbleViewCallback mBubbleViewCallback;
+ private final BubbleTransitions mBubbleTransitions;
+
public BubbleController(Context context,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
@@ -350,12 +353,16 @@ public class BubbleController implements ConfigurationChangeListener,
context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.importance_ring_stroke_width));
mDisplayController = displayController;
+ final TaskViewTransitions tvTransitions;
if (TaskViewTransitions.useRepo()) {
- mTaskViewController = new TaskViewTransitions(transitions, taskViewRepository,
- organizer, syncQueue);
+ tvTransitions = new TaskViewTransitions(transitions, taskViewRepository, organizer,
+ syncQueue);
} else {
- mTaskViewController = taskViewTransitions;
+ tvTransitions = taskViewTransitions;
}
+ mTaskViewController = tvTransitions;
+ mBubbleTransitions = new BubbleTransitions(transitions, organizer, taskViewRepository, data,
+ tvTransitions, context);
mTransitions = transitions;
mOneHandedOptional = oneHandedOptional;
mDragAndDropController = dragAndDropController;
@@ -1456,7 +1463,19 @@ public class BubbleController implements ConfigurationChangeListener,
* @param taskInfo the task.
*/
public void expandStackAndSelectBubble(ActivityManager.RunningTaskInfo taskInfo) {
- // TODO(384976265): Not implemented yet
+ if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) return;
+ Bubble b = mBubbleData.getOrCreateBubble(taskInfo); // Removes from overflow
+ ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - intent=%s", taskInfo.taskId);
+ if (b.isInflated()) {
+ mBubbleData.setSelectedBubbleAndExpandStack(b);
+ } else {
+ b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
+ // Lazy init stack view when a bubble is created
+ ensureBubbleViewsAndWindowCreated();
+ mBubbleTransitions.startConvertToBubble(b, taskInfo, mExpandedViewManager,
+ mBubbleTaskViewFactory, mBubblePositioner, mLogger, mStackView, mLayerView,
+ mBubbleIconFactory, mInflateSynchronously);
+ }
}
/**
@@ -2261,9 +2280,16 @@ public class BubbleController implements ConfigurationChangeListener,
private void showExpandedViewForBubbleBar() {
BubbleViewProvider selectedBubble = mBubbleData.getSelectedBubble();
- if (selectedBubble != null && mLayerView != null) {
- mLayerView.showExpandedView(selectedBubble);
+ if (selectedBubble == null) return;
+ if (selectedBubble instanceof Bubble) {
+ final Bubble bubble = (Bubble) selectedBubble;
+ if (bubble.getPreparingTransition() != null) {
+ bubble.getPreparingTransition().continueExpand();
+ return;
+ }
}
+ if (mLayerView == null) return;
+ mLayerView.showExpandedView(selectedBubble);
}
private void collapseExpandedViewForBubbleBar() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 74302094a296..96d0f6d5654e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -22,6 +22,7 @@ import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
import android.annotation.NonNull;
import android.app.PendingIntent;
+import android.app.TaskInfo;
import android.content.Context;
import android.content.Intent;
import android.content.LocusId;
@@ -470,6 +471,17 @@ public class BubbleData {
return bubbleToReturn;
}
+ Bubble getOrCreateBubble(TaskInfo taskInfo) {
+ UserHandle user = UserHandle.of(mCurrentUserId);
+ String bubbleKey = Bubble.getAppBubbleKeyForTask(taskInfo);
+ Bubble bubbleToReturn = findAndRemoveBubbleFromOverflow(bubbleKey);
+ if (bubbleToReturn == null) {
+ bubbleToReturn = Bubble.createTaskBubble(taskInfo, user, null, mMainExecutor,
+ mBgExecutor);
+ }
+ return bubbleToReturn;
+ }
+
@Nullable
private Bubble findAndRemoveBubbleFromOverflow(String key) {
Bubble bubbleToReturn = getBubbleInStackWithKey(key);
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 4e7f87c48a86..f1f49eda75b6 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
@@ -633,8 +633,6 @@ public class BubbleStackView extends FrameLayout
mMagneticTarget,
mIndividualBubbleMagnetListener);
- hideCurrentInputMethod();
-
// Save the magnetized individual bubble so we can dispatch touch events to it.
mMagnetizedObject = mExpandedAnimationController.getMagnetizedBubbleDraggingOut();
} else {
@@ -671,6 +669,10 @@ public class BubbleStackView extends FrameLayout
return;
}
+ if (mPositioner.isImeVisible()) {
+ hideCurrentInputMethod();
+ }
+
// Show the dismiss target, if we haven't already.
mDismissView.show();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index 89c038b4a26b..ae84f449c0e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
@@ -109,7 +109,9 @@ public class BubbleTaskViewHelper {
MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
final boolean isShortcutBubble = (mBubble.hasMetadataShortcutId()
|| (mBubble.getShortcutInfo() != null && Flags.enableBubbleAnything()));
- if (mBubble.isAppBubble()) {
+ if (mBubble.getPreparingTransition() != null) {
+ mBubble.getPreparingTransition().surfaceCreated();
+ } else if (mBubble.isAppBubble()) {
Context context =
mContext.createContextAsUser(
mBubble.getUser(), Context.CONTEXT_RESTRICTED);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
new file mode 100644
index 000000000000..e37844f53b11
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.view.WindowManager.TRANSIT_CHANGE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.view.SurfaceView;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.launcher3.icons.BubbleIconFactory;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
+import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+import com.android.wm.shell.taskview.TaskView;
+import com.android.wm.shell.taskview.TaskViewRepository;
+import com.android.wm.shell.taskview.TaskViewTaskController;
+import com.android.wm.shell.taskview.TaskViewTransitions;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Implements transition coordination for bubble operations.
+ */
+public class BubbleTransitions {
+ private static final String TAG = "BubbleTransitions";
+
+ @NonNull final Transitions mTransitions;
+ @NonNull final ShellTaskOrganizer mTaskOrganizer;
+ @NonNull final TaskViewRepository mRepository;
+ @NonNull final Executor mMainExecutor;
+ @NonNull final BubbleData mBubbleData;
+ @NonNull final TaskViewTransitions mTaskViewTransitions;
+ @NonNull final Context mContext;
+
+ BubbleTransitions(@NonNull Transitions transitions, @NonNull ShellTaskOrganizer organizer,
+ @NonNull TaskViewRepository repository, @NonNull BubbleData bubbleData,
+ @NonNull TaskViewTransitions taskViewTransitions, Context context) {
+ mTransitions = transitions;
+ mTaskOrganizer = organizer;
+ mRepository = repository;
+ mMainExecutor = transitions.getMainExecutor();
+ mBubbleData = bubbleData;
+ mTaskViewTransitions = taskViewTransitions;
+ mContext = context;
+ }
+
+ /**
+ * Starts a convert-to-bubble transition.
+ *
+ * @see ConvertToBubble
+ */
+ public BubbleTransition startConvertToBubble(Bubble bubble, TaskInfo taskInfo,
+ BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory,
+ BubblePositioner positioner, BubbleLogger logger, BubbleStackView stackView,
+ BubbleBarLayerView layerView, BubbleIconFactory iconFactory,
+ boolean inflateSync) {
+ ConvertToBubble convert = new ConvertToBubble(bubble, taskInfo, mContext,
+ expandedViewManager, factory, positioner, logger, stackView, layerView, iconFactory,
+ inflateSync);
+ return convert;
+ }
+
+ /**
+ * Interface to a bubble-specific transition. Bubble transitions have a multi-step lifecycle
+ * in order to coordinate with the bubble view logic. These steps are communicated on this
+ * interface.
+ */
+ interface BubbleTransition {
+ default void surfaceCreated() {}
+ default void continueExpand() {}
+ void skip();
+ }
+
+ /**
+ * BubbleTransition that coordinates the process of a non-bubble task becoming a bubble. The
+ * steps are as follows:
+ *
+ * 1. Start inflating the bubble view
+ * 2. Once inflated (but not-yet visible), tell WM to do the shell-transition.
+ * 3. Transition becomes ready, so notify Launcher
+ * 4. Launcher responds with showExpandedView which calls continueExpand() to make view visible
+ * 5. Surface is created which kicks off actual animation
+ *
+ * So, constructor -> onInflated -> startAnimation -> continueExpand -> surfaceCreated.
+ *
+ * continueExpand and surfaceCreated are set-up to happen in either order, though, to support
+ * UX/timing adjustments.
+ */
+ @VisibleForTesting
+ class ConvertToBubble implements Transitions.TransitionHandler, BubbleTransition {
+ final BubbleBarLayerView mLayerView;
+ Bubble mBubble;
+ IBinder mTransition;
+ Transitions.TransitionFinishCallback mFinishCb;
+ WindowContainerTransaction mFinishWct = null;
+ final Rect mStartBounds = new Rect();
+ SurfaceControl mSnapshot = null;
+ TaskInfo mTaskInfo;
+ boolean mFinishedExpand = false;
+ BubbleViewProvider mPriorBubble = null;
+
+ private SurfaceControl.Transaction mFinishT;
+ private SurfaceControl mTaskLeash;
+
+ ConvertToBubble(Bubble bubble, TaskInfo taskInfo, Context context,
+ BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory,
+ BubblePositioner positioner, BubbleLogger logger, BubbleStackView stackView,
+ BubbleBarLayerView layerView, BubbleIconFactory iconFactory, boolean inflateSync) {
+ mBubble = bubble;
+ mTaskInfo = taskInfo;
+ mLayerView = layerView;
+ mBubble.setInflateSynchronously(inflateSync);
+ mBubble.setPreparingTransition(this);
+ mBubble.inflate(
+ this::onInflated,
+ context,
+ expandedViewManager,
+ factory,
+ positioner,
+ logger,
+ stackView,
+ layerView,
+ iconFactory,
+ false /* skipInflation */);
+ }
+
+ @VisibleForTesting
+ void onInflated(Bubble b) {
+ if (b != mBubble) {
+ throw new IllegalArgumentException("inflate callback doesn't match bubble");
+ }
+ final Rect launchBounds = new Rect();
+ mLayerView.getExpandedViewRestBounds(launchBounds);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
+ if (mTaskInfo.getParentTaskId() != INVALID_TASK_ID) {
+ wct.reparent(mTaskInfo.token, null, true);
+ }
+ }
+
+ wct.setAlwaysOnTop(mTaskInfo.token, true);
+ wct.setWindowingMode(mTaskInfo.token, WINDOWING_MODE_MULTI_WINDOW);
+ wct.setBounds(mTaskInfo.token, launchBounds);
+
+ final TaskView tv = b.getTaskView();
+ tv.setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT);
+ final TaskViewRepository.TaskViewState state = mRepository.byTaskView(
+ tv.getController());
+ if (state != null) {
+ state.mVisible = true;
+ }
+ mTaskViewTransitions.enqueueExternal(tv.getController(), () -> {
+ mTransition = mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
+ return mTransition;
+ });
+ }
+
+ @Override
+ public void skip() {
+ mBubble.setPreparingTransition(null);
+ mFinishCb.onTransitionFinished(mFinishWct);
+ mFinishCb = null;
+ }
+
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @Nullable TransitionRequestInfo request) {
+ return null;
+ }
+
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ }
+
+ @Override
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @NonNull SurfaceControl.Transaction finishTransaction) {
+ if (!aborted) return;
+ mTransition = null;
+ mTaskViewTransitions.onExternalDone(transition);
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (mTransition != transition) return false;
+ boolean found = false;
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change chg = info.getChanges().get(i);
+ if (chg.getTaskInfo() == null) continue;
+ if (chg.getMode() != TRANSIT_CHANGE) continue;
+ if (!mTaskInfo.token.equals(chg.getTaskInfo().token)) continue;
+ mStartBounds.set(chg.getStartAbsBounds());
+ // Converting a task into taskview, so treat as "new"
+ mFinishWct = new WindowContainerTransaction();
+ mTaskInfo = chg.getTaskInfo();
+ mFinishT = finishTransaction;
+ mTaskLeash = chg.getLeash();
+ found = true;
+ mSnapshot = chg.getSnapshot();
+ break;
+ }
+ if (!found) {
+ Slog.w(TAG, "Expected a TaskView conversion in this transition but didn't get "
+ + "one, cleaning up the task view");
+ mBubble.getTaskView().getController().setTaskNotFound();
+ mTaskViewTransitions.onExternalDone(transition);
+ return false;
+ }
+ mFinishCb = finishCallback;
+
+ // Now update state (and talk to launcher) in parallel with snapshot stuff
+ mBubbleData.notificationEntryUpdated(mBubble, /* suppressFlyout= */ true,
+ /* showInShade= */ false);
+
+ startTransaction.show(mSnapshot);
+ // Move snapshot to root so that it remains visible while task is moved to taskview
+ startTransaction.reparent(mSnapshot, info.getRoot(0).getLeash());
+ startTransaction.setPosition(mSnapshot,
+ mStartBounds.left - info.getRoot(0).getOffset().x,
+ mStartBounds.top - info.getRoot(0).getOffset().y);
+ startTransaction.setLayer(mSnapshot, Integer.MAX_VALUE);
+ startTransaction.apply();
+
+ mTaskViewTransitions.onExternalDone(transition);
+ return true;
+ }
+
+ @Override
+ public void continueExpand() {
+ mFinishedExpand = true;
+ final boolean animate = mLayerView.canExpandView(mBubble);
+ if (animate) {
+ mPriorBubble = mLayerView.prepareConvertedView(mBubble);
+ }
+ if (mPriorBubble != null) {
+ // TODO: an animation. For now though, just remove it.
+ final BubbleBarExpandedView priorView = mPriorBubble.getBubbleBarExpandedView();
+ mLayerView.removeView(priorView);
+ mPriorBubble = null;
+ }
+ if (!animate || mBubble.getTaskView().getSurfaceControl() != null) {
+ playAnimation(animate);
+ }
+ }
+
+ @Override
+ public void surfaceCreated() {
+ mMainExecutor.execute(() -> {
+ final TaskViewTaskController tvc = mBubble.getTaskView().getController();
+ final TaskViewRepository.TaskViewState state = mRepository.byTaskView(tvc);
+ if (state == null) return;
+ state.mVisible = true;
+ if (mFinishedExpand) {
+ playAnimation(true /* animate */);
+ }
+ });
+ }
+
+ private void playAnimation(boolean animate) {
+ final TaskViewTaskController tv = mBubble.getTaskView().getController();
+ final SurfaceControl.Transaction startT = new SurfaceControl.Transaction();
+ mTaskViewTransitions.prepareOpenAnimation(tv, true /* new */, startT, mFinishT,
+ (ActivityManager.RunningTaskInfo) mTaskInfo, mTaskLeash, mFinishWct);
+
+ if (mFinishWct.isEmpty()) {
+ mFinishWct = null;
+ }
+
+ // Preparation is complete.
+ mBubble.setPreparingTransition(null);
+
+ if (animate) {
+ mLayerView.animateConvert(startT, mStartBounds, mSnapshot, mTaskLeash, () -> {
+ mFinishCb.onTransitionFinished(mFinishWct);
+ mFinishCb = null;
+ });
+ } else {
+ startT.apply();
+ mFinishCb.onTransitionFinished(mFinishWct);
+ mFinishCb = null;
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index de6d1f6c8852..52f20646fb4a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -36,17 +36,21 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Log;
import android.util.Size;
+import android.view.SurfaceControl;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
import com.android.app.animation.Interpolators;
import com.android.wm.shell.R;
+import com.android.wm.shell.animation.SizeChangeAnimation;
+import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleViewProvider;
@@ -571,6 +575,49 @@ public class BubbleBarAnimationHelper {
}
/**
+ * Animates converting of a non-bubble task into an expanded bubble view.
+ */
+ public void animateConvert(BubbleViewProvider expandedBubble,
+ @NonNull SurfaceControl.Transaction startT,
+ @NonNull Rect origBounds,
+ @NonNull SurfaceControl snapshot,
+ @NonNull SurfaceControl taskLeash,
+ @Nullable Runnable afterAnimation) {
+ mExpandedBubble = expandedBubble;
+ final BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev == null) {
+ return;
+ }
+
+ bbev.setTaskViewAlpha(1f);
+ SurfaceControl tvSf = ((Bubble) mExpandedBubble).getTaskView().getSurfaceControl();
+
+ final Size size = getExpandedViewSize();
+ Point position = getExpandedViewRestPosition(size);
+
+ final SizeChangeAnimation sca =
+ new SizeChangeAnimation(
+ new Rect(origBounds.left - position.x, origBounds.top - position.y,
+ origBounds.right - position.x, origBounds.bottom - position.y),
+ new Rect(0, 0, size.getWidth(), size.getHeight()));
+ sca.initialize(bbev, taskLeash, snapshot, startT);
+
+ Animator a = sca.buildViewAnimator(bbev, tvSf, snapshot, /* onFinish */ (va) -> {
+ updateExpandedView(bbev);
+ snapshot.release();
+ bbev.setSurfaceZOrderedOnTop(false);
+ bbev.setAnimating(false);
+ if (afterAnimation != null) {
+ afterAnimation.run();
+ }
+ });
+
+ bbev.setSurfaceZOrderedOnTop(true);
+ a.setDuration(EXPANDED_VIEW_ANIMATE_TO_REST_DURATION);
+ a.start();
+ }
+
+ /**
* Cancel current animations
*/
public void cancelAnimations() {
@@ -627,6 +674,13 @@ public class BubbleBarAnimationHelper {
bbev.maybeShowOverflow();
}
+ void getExpandedViewRestBounds(Rect out) {
+ final int width = mPositioner.getExpandedViewWidthForBubbleBar(false /* overflow */);
+ final int height = mPositioner.getExpandedViewHeightForBubbleBar(false /* overflow */);
+ Point position = getExpandedViewRestPosition(new Size(width, height));
+ out.set(position.x, position.y, position.x + width, position.y + height);
+ }
+
private Point getExpandedViewRestPosition(Size size) {
final int padding = mPositioner.getBubbleBarExpandedViewPadding();
Point point = new Point();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index eaa0bd250fc4..91dcbdf5f117 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -28,6 +28,7 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
+import android.view.SurfaceControl;
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewTreeObserver;
@@ -174,14 +175,34 @@ public class BubbleBarLayerView extends FrameLayout
/** Shows the expanded view of the provided bubble. */
public void showExpandedView(BubbleViewProvider b) {
- BubbleBarExpandedView expandedView = b.getBubbleBarExpandedView();
- if (expandedView == null) {
- return;
- }
+ if (!canExpandView(b)) return;
+ animateExpand(prepareExpandedView(b));
+ }
+
+ /**
+ * @return whether it's possible to expand {@param b} right now. This is {@code false} if
+ * the bubble has no view or if the bubble is already showing.
+ */
+ public boolean canExpandView(BubbleViewProvider b) {
+ if (b.getBubbleBarExpandedView() == null) return false;
if (mExpandedBubble != null && mIsExpanded && b.getKey().equals(mExpandedBubble.getKey())) {
- // Already showing this bubble, skip animating
- return;
+ // Already showing this bubble so can't expand it.
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Prepares the expanded view of the provided bubble to be shown. This includes removing any
+ * stale content and cancelling any related animations.
+ *
+ * @return previous open bubble if there was one.
+ */
+ private BubbleViewProvider prepareExpandedView(BubbleViewProvider b) {
+ if (!canExpandView(b)) {
+ throw new IllegalStateException("Can't prepare expand. Check canExpandView(b) first.");
}
+ BubbleBarExpandedView expandedView = b.getBubbleBarExpandedView();
BubbleViewProvider previousBubble = null;
if (mExpandedBubble != null && !b.getKey().equals(mExpandedBubble.getKey())) {
if (mIsExpanded && mExpandedBubble.getBubbleBarExpandedView() != null) {
@@ -251,7 +272,20 @@ public class BubbleBarLayerView extends FrameLayout
mIsExpanded = true;
mBubbleController.getSysuiProxy().onStackExpandChanged(true);
+ showScrim(true);
+ return previousBubble;
+ }
+ /**
+ * Performs an animation to open a bubble with content that is not already visible.
+ *
+ * @param previousBubble If non-null, this is a bubble that is already showing before the new
+ * bubble is expanded.
+ */
+ public void animateExpand(BubbleViewProvider previousBubble) {
+ if (!mIsExpanded || mExpandedBubble == null) {
+ throw new IllegalStateException("Can't animateExpand without expnaded state");
+ }
final Runnable afterAnimation = () -> {
if (mExpandedView == null) return;
// Touch delegate for the menu
@@ -274,8 +308,49 @@ public class BubbleBarLayerView extends FrameLayout
} else {
mAnimationHelper.animateExpansion(mExpandedBubble, afterAnimation);
}
+ }
- showScrim(true);
+ /**
+ * Like {@link #prepareExpandedView} but also makes the current expanded bubble visible
+ * immediately so it gets a surface that can be animated. Since the surface may not be ready
+ * yet, this keeps the TaskView alpha=0.
+ */
+ public BubbleViewProvider prepareConvertedView(BubbleViewProvider b) {
+ final BubbleViewProvider prior = prepareExpandedView(b);
+
+ final BubbleBarExpandedView bbev = mExpandedBubble.getBubbleBarExpandedView();
+ if (bbev != null) {
+ updateExpandedView();
+ bbev.setAnimating(true);
+ bbev.setContentVisibility(true);
+ bbev.setSurfaceZOrderedOnTop(true);
+ bbev.setTaskViewAlpha(0.f);
+ bbev.setVisibility(VISIBLE);
+ }
+
+ return prior;
+ }
+
+ /**
+ * Starts and animates a conversion-from transition.
+ *
+ * @param startT A transaction with first-frame work. this *will* be applied here!
+ */
+ public void animateConvert(@NonNull SurfaceControl.Transaction startT,
+ @NonNull Rect startBounds, @NonNull SurfaceControl snapshot, SurfaceControl taskLeash,
+ Runnable animFinish) {
+ if (!mIsExpanded || mExpandedBubble == null) {
+ throw new IllegalStateException("Can't animateExpand without expanded state");
+ }
+ mAnimationHelper.animateConvert(mExpandedBubble, startT, startBounds, snapshot, taskLeash,
+ animFinish);
+ }
+
+ /**
+ * Populates {@param out} with the rest bounds of an expanded bubble.
+ */
+ public void getExpandedViewRestBounds(Rect out) {
+ mAnimationHelper.getExpandedViewRestBounds(out);
}
/** Removes the given {@code bubble}. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/UserProfileContexts.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/UserProfileContexts.kt
new file mode 100644
index 000000000000..0577f9e625ca
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/UserProfileContexts.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.common
+
+import android.app.ActivityManager
+import android.content.Context
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import android.os.UserManager
+import android.util.SparseArray
+import com.android.wm.shell.sysui.ShellController
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.sysui.UserChangeListener
+
+/** Creates and manages contexts for all the profiles of the current user. */
+class UserProfileContexts(
+ private val baseContext: Context,
+ private val shellController: ShellController,
+ shellInit: ShellInit,
+) {
+ // Contexts for all the profiles of the current user.
+ private val currentProfilesContext = SparseArray<Context>()
+
+ lateinit var userContext: Context
+ private set
+
+ init {
+ shellInit.addInitCallback(this::onInit, this)
+ }
+
+ private fun onInit() {
+ shellController.addUserChangeListener(
+ object : UserChangeListener {
+ override fun onUserChanged(newUserId: Int, userContext: Context) {
+ currentProfilesContext.clear()
+ this@UserProfileContexts.userContext = userContext
+ currentProfilesContext.put(newUserId, userContext)
+ }
+
+ override fun onUserProfilesChanged(profiles: List<UserInfo>) {
+ updateProfilesContexts(profiles)
+ }
+ }
+ )
+ val defaultUserId = ActivityManager.getCurrentUser()
+ val userManager = baseContext.getSystemService(UserManager::class.java)
+ userContext = baseContext.createContextAsUser(UserHandle.of(defaultUserId), /* flags= */ 0)
+ updateProfilesContexts(userManager.getProfiles(defaultUserId))
+ }
+
+ private fun updateProfilesContexts(profiles: List<UserInfo>) {
+ for (profile in profiles) {
+ if (profile.id in currentProfilesContext) continue
+ val profileContext = baseContext.createContextAsUser(profile.userHandle, /* flags= */ 0)
+ currentProfilesContext.put(profile.id, profileContext)
+ }
+ val profilesToRemove = buildList<Int> {
+ for (i in 0..<currentProfilesContext.size()) {
+ val userId = currentProfilesContext.keyAt(i)
+ if (profiles.none { it.id == userId }) {
+ add(userId)
+ }
+ }
+ }
+ profilesToRemove.forEach { currentProfilesContext.remove(it) }
+ }
+
+ operator fun get(userId: Int): Context? = currentProfilesContext.get(userId)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index 83e5e31bd125..84c30a52e8a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -76,8 +76,7 @@ public class SplitScreenUtils {
* Returns whether left/right split is allowed in portrait.
*/
public static boolean allowLeftRightSplitInPortrait(Resources res) {
- return Flags.enableLeftRightSplitInPortrait() && res.getBoolean(
- com.android.internal.R.bool.config_leftRightSplitInPortrait);
+ return res.getBoolean(com.android.internal.R.bool.config_leftRightSplitInPortrait);
}
/**
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 67e345365d26..1bdb5922a102 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
@@ -70,6 +70,7 @@ import com.android.wm.shell.common.MultiInstanceHelper;
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.common.UserProfileContexts;
import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler;
import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver;
@@ -95,6 +96,7 @@ import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
+import com.android.wm.shell.desktopmode.OverviewToDesktopTransitionObserver;
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator;
import com.android.wm.shell.desktopmode.SpringDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
@@ -107,6 +109,8 @@ import com.android.wm.shell.desktopmode.education.AppToWebEducationController;
import com.android.wm.shell.desktopmode.education.AppToWebEducationFilter;
import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository;
import com.android.wm.shell.desktopmode.education.data.AppToWebEducationDatastoreRepository;
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer;
+import com.android.wm.shell.desktopmode.multidesks.RootTaskDesksOrganizer;
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository;
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer;
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializerImpl;
@@ -394,6 +398,7 @@ public abstract class WMShellModule {
ShellTaskOrganizer shellTaskOrganizer,
Optional<DesktopUserRepositories> desktopUserRepositories,
Optional<DesktopTasksController> desktopTasksController,
+ DesktopModeLoggerTransitionObserver desktopModeLoggerTransitionObserver,
LaunchAdjacentController launchAdjacentController,
WindowDecorViewModel windowDecorViewModel,
Optional<TaskChangeListener> taskChangeListener) {
@@ -406,6 +411,7 @@ public abstract class WMShellModule {
shellTaskOrganizer,
desktopUserRepositories,
desktopTasksController,
+ desktopModeLoggerTransitionObserver,
launchAdjacentController,
windowDecorViewModel,
taskChangeListener);
@@ -702,6 +708,16 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
+ static DesksOrganizer provideDesksOrganizer(
+ @NonNull ShellInit shellInit,
+ @NonNull ShellCommandHandler shellCommandHandler,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer
+ ) {
+ return new RootTaskDesksOrganizer(shellInit, shellCommandHandler, shellTaskOrganizer);
+ }
+
+ @WMSingleton
+ @Provides
@DynamicOverride
static DesktopTasksController provideDesktopTasksController(
Context context,
@@ -739,7 +755,10 @@ public abstract class WMShellModule {
DesktopModeUiEventLogger desktopModeUiEventLogger,
DesktopTilingDecorViewModel desktopTilingDecorViewModel,
DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
- Optional<BubbleController> bubbleController) {
+ Optional<BubbleController> bubbleController,
+ OverviewToDesktopTransitionObserver overviewToDesktopTransitionObserver,
+ DesksOrganizer desksOrganizer,
+ UserProfileContexts userProfileContexts) {
return new DesktopTasksController(
context,
shellInit,
@@ -772,7 +791,10 @@ public abstract class WMShellModule {
desktopModeUiEventLogger,
desktopTilingDecorViewModel,
desktopWallpaperActivityTokenProvider,
- bubbleController);
+ bubbleController,
+ overviewToDesktopTransitionObserver,
+ desksOrganizer,
+ userProfileContexts);
}
@WMSingleton
@@ -974,9 +996,10 @@ public abstract class WMShellModule {
static WindowDecorTaskResourceLoader provideWindowDecorTaskResourceLoader(
@NonNull Context context, @NonNull ShellInit shellInit,
@NonNull ShellController shellController,
- @NonNull ShellCommandHandler shellCommandHandler) {
+ @NonNull ShellCommandHandler shellCommandHandler,
+ @NonNull UserProfileContexts userProfileContexts) {
return new WindowDecorTaskResourceLoader(context, shellInit, shellController,
- shellCommandHandler);
+ shellCommandHandler, userProfileContexts);
}
@WMSingleton
@@ -1180,10 +1203,11 @@ public abstract class WMShellModule {
Transitions transitions,
DisplayController displayController,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
- IWindowManager windowManager
+ IWindowManager windowManager,
+ Optional<DesktopUserRepositories> desktopUserRepositories,
+ Optional<DesktopTasksController> desktopTasksController
) {
- if (!DesktopModeStatus.canEnterDesktopMode(context)
- || !Flags.enableDisplayWindowingModeSwitching()) {
+ if (!DesktopModeStatus.canEnterDesktopMode(context)) {
return Optional.empty();
}
return Optional.of(
@@ -1193,7 +1217,9 @@ public abstract class WMShellModule {
transitions,
displayController,
rootTaskDisplayAreaOrganizer,
- windowManager));
+ windowManager,
+ desktopUserRepositories.get(),
+ desktopTasksController.get()));
}
@WMSingleton
@@ -1397,4 +1423,20 @@ public abstract class WMShellModule {
return new Object();
}
+ @WMSingleton
+ @Provides
+ static OverviewToDesktopTransitionObserver provideOverviewToDesktopTransitionObserver(
+ Transitions transitions, ShellInit shellInit) {
+ return new OverviewToDesktopTransitionObserver(transitions, shellInit);
+ }
+
+ @WMSingleton
+ @Provides
+ static UserProfileContexts provideUserProfilesContexts(
+ Context context,
+ ShellController shellController,
+ ShellInit shellInit) {
+ return new UserProfileContexts(context, shellController, shellInit);
+ }
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
index 43e8d2a30930..760d2124b845 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
@@ -24,9 +24,14 @@ import android.view.Display.DEFAULT_DISPLAY
import android.view.IWindowManager
import android.view.WindowManager.TRANSIT_CHANGE
import android.window.WindowContainerTransaction
+import com.android.internal.protolog.ProtoLog
+import com.android.window.flags.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
+import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
@@ -38,7 +43,12 @@ class DesktopDisplayEventHandler(
private val displayController: DisplayController,
private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
private val windowManager: IWindowManager,
-) : OnDisplaysChangedListener {
+ private val desktopUserRepositories: DesktopUserRepositories,
+ private val desktopTasksController: DesktopTasksController,
+) : OnDisplaysChangedListener, OnDeskRemovedListener {
+
+ private val desktopRepository: DesktopRepository
+ get() = desktopUserRepositories.current
init {
shellInit.addInitCallback({ onInit() }, this)
@@ -46,23 +56,43 @@ class DesktopDisplayEventHandler(
private fun onInit() {
displayController.addDisplayWindowListener(this)
+
+ if (Flags.enableMultipleDesktopsBackend()) {
+ desktopTasksController.onDeskRemovedListener = this
+ }
}
override fun onDisplayAdded(displayId: Int) {
- if (displayId == DEFAULT_DISPLAY) {
+ if (displayId != DEFAULT_DISPLAY) {
+ refreshDisplayWindowingMode()
+ }
+
+ if (!supportsDesks(displayId)) {
+ logV("Display #$displayId does not support desks")
return
}
- refreshDisplayWindowingMode()
+ logV("Creating new desk in new display#$displayId")
+ desktopTasksController.createDesk(displayId)
}
override fun onDisplayRemoved(displayId: Int) {
- if (displayId == DEFAULT_DISPLAY) {
- return
+ if (displayId != DEFAULT_DISPLAY) {
+ refreshDisplayWindowingMode()
+ }
+
+ // TODO: b/362720497 - move desks in closing display to the remaining desk.
+ }
+
+ override fun onDeskRemoved(lastDisplayId: Int, deskId: Int) {
+ val remainingDesks = desktopRepository.getNumberOfDesks(lastDisplayId)
+ if (remainingDesks == 0) {
+ logV("All desks removed from display#$lastDisplayId, creating empty desk")
+ desktopTasksController.createDesk(lastDisplayId)
}
- refreshDisplayWindowingMode()
}
private fun refreshDisplayWindowingMode() {
+ if (!Flags.enableDisplayWindowingModeSwitching()) return
// TODO: b/375319538 - Replace the check with a DisplayManager API once it's available.
val isExtendedDisplayEnabled =
0 !=
@@ -98,4 +128,16 @@ class DesktopDisplayEventHandler(
wct.setWindowingMode(tdaInfo.token, targetDisplayWindowingMode)
transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
}
+
+ // TODO: b/362720497 - connected/projected display considerations.
+ private fun supportsDesks(displayId: Int): Boolean =
+ DesktopModeStatus.canEnterDesktopMode(context)
+
+ private fun logV(msg: String, vararg arguments: Any?) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ companion object {
+ private const val TAG = "DesktopDisplayEventHandler"
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index a43358603bc3..3b051694ae81 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -167,6 +167,29 @@ class DesktopModeLoggerTransitionObserver(
override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {}
+ fun onTaskVanished(taskInfo: RunningTaskInfo) {
+ // At this point the task should have been cleared up due to transition. If it's not yet
+ // cleared up, it might be one of the edge cases where transitions don't give the correct
+ // signal.
+ if (visibleFreeformTaskInfos.containsKey(taskInfo.taskId)) {
+ val postTransitionFreeformTasks: SparseArray<TaskInfo> = SparseArray()
+ postTransitionFreeformTasks.putAll(visibleFreeformTaskInfos)
+ postTransitionFreeformTasks.remove(taskInfo.taskId)
+ ProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopModeLogger: processing tasks after task vanished %s",
+ postTransitionFreeformTasks.size(),
+ )
+ identifyLogEventAndUpdateState(
+ transition = null,
+ transitionInfo = null,
+ preTransitionVisibleFreeformTasks = visibleFreeformTaskInfos,
+ postTransitionVisibleFreeformTasks = postTransitionFreeformTasks,
+ newFocusedFreeformTask = null,
+ )
+ }
+ }
+
// Returns null if there was no change in focused task
private fun getNewFocusedFreeformTask(info: TransitionInfo): TaskInfo? {
val freeformWindowChanges =
@@ -253,8 +276,8 @@ class DesktopModeLoggerTransitionObserver(
* state and update it
*/
private fun identifyLogEventAndUpdateState(
- transition: IBinder,
- transitionInfo: TransitionInfo,
+ transition: IBinder?,
+ transitionInfo: TransitionInfo?,
preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
newFocusedFreeformTask: TaskInfo?,
@@ -310,8 +333,8 @@ class DesktopModeLoggerTransitionObserver(
/** Compare the old and new state of taskInfos and identify and log the changes */
private fun identifyAndLogTaskUpdates(
- transition: IBinder,
- transitionInfo: TransitionInfo,
+ transition: IBinder?,
+ transitionInfo: TransitionInfo?,
preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
newFocusedFreeformTask: TaskInfo?,
@@ -384,22 +407,24 @@ class DesktopModeLoggerTransitionObserver(
}
private fun getMinimizeReason(
- transition: IBinder,
- transitionInfo: TransitionInfo,
+ transition: IBinder?,
+ transitionInfo: TransitionInfo?,
taskInfo: TaskInfo,
): MinimizeReason? {
- if (transitionInfo.type == Transitions.TRANSIT_MINIMIZE) {
+ if (transitionInfo?.type == Transitions.TRANSIT_MINIMIZE) {
return MinimizeReason.MINIMIZE_BUTTON
}
- val minimizingTask = desktopTasksLimiter.getOrNull()?.getMinimizingTask(transition)
+ val minimizingTask =
+ transition?.let { desktopTasksLimiter.getOrNull()?.getMinimizingTask(transition) }
if (minimizingTask?.taskId == taskInfo.taskId) {
return minimizingTask.minimizeReason
}
return null
}
- private fun getUnminimizeReason(transition: IBinder, taskInfo: TaskInfo): UnminimizeReason? {
- val unminimizingTask = desktopTasksLimiter.getOrNull()?.getUnminimizingTask(transition)
+ private fun getUnminimizeReason(transition: IBinder?, taskInfo: TaskInfo): UnminimizeReason? {
+ val unminimizingTask =
+ transition?.let { desktopTasksLimiter.getOrNull()?.getUnminimizingTask(transition) }
if (unminimizingTask?.taskId == taskInfo.taskId) {
return unminimizingTask.unminimizeReason
}
@@ -441,24 +466,24 @@ class DesktopModeLoggerTransitionObserver(
}
/** Get [EnterReason] for this session enter */
- private fun getEnterReason(transitionInfo: TransitionInfo): EnterReason {
+ private fun getEnterReason(transitionInfo: TransitionInfo?): EnterReason {
val enterReason =
when {
- transitionInfo.type == WindowManager.TRANSIT_WAKE
+ transitionInfo?.type == WindowManager.TRANSIT_WAKE
// If there is a screen lock, desktop window entry is after dismissing keyguard
||
- (transitionInfo.type == WindowManager.TRANSIT_TO_BACK &&
+ (transitionInfo?.type == WindowManager.TRANSIT_TO_BACK &&
wasPreviousTransitionExitByScreenOff) -> EnterReason.SCREEN_ON
- transitionInfo.type == TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP ->
+ transitionInfo?.type == TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP ->
EnterReason.APP_HANDLE_DRAG
- transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON ->
+ transitionInfo?.type == TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON ->
EnterReason.APP_HANDLE_MENU_BUTTON
- transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW ->
+ transitionInfo?.type == TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW ->
EnterReason.APP_FROM_OVERVIEW
- transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT ->
+ transitionInfo?.type == TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT ->
EnterReason.KEYBOARD_SHORTCUT_ENTER
// NOTE: the below condition also applies for EnterReason quickswitch
- transitionInfo.type == WindowManager.TRANSIT_TO_FRONT -> EnterReason.OVERVIEW
+ transitionInfo?.type == WindowManager.TRANSIT_TO_FRONT -> EnterReason.OVERVIEW
// Enter desktop mode from cancelled recents has no transition. Enter is detected on
// the
// next transition involving freeform windows.
@@ -469,12 +494,13 @@ class DesktopModeLoggerTransitionObserver(
// after
// a cancelled recents.
wasPreviousTransitionExitToOverview -> EnterReason.OVERVIEW
- transitionInfo.type == WindowManager.TRANSIT_OPEN -> EnterReason.APP_FREEFORM_INTENT
+ transitionInfo?.type == WindowManager.TRANSIT_OPEN ->
+ EnterReason.APP_FREEFORM_INTENT
else -> {
ProtoLog.w(
WM_SHELL_DESKTOP_MODE,
"Unknown enter reason for transition type: %s",
- transitionInfo.type,
+ transitionInfo?.type,
)
EnterReason.UNKNOWN_ENTER
}
@@ -484,30 +510,31 @@ class DesktopModeLoggerTransitionObserver(
}
/** Get [ExitReason] for this session exit */
- private fun getExitReason(transitionInfo: TransitionInfo): ExitReason =
+ private fun getExitReason(transitionInfo: TransitionInfo?): ExitReason =
when {
- transitionInfo.type == WindowManager.TRANSIT_SLEEP -> {
+ transitionInfo?.type == WindowManager.TRANSIT_SLEEP -> {
wasPreviousTransitionExitByScreenOff = true
ExitReason.SCREEN_OFF
}
// TODO(b/384490301): differentiate back gesture / button exit from clicking the close
// button located in the window top corner.
- transitionInfo.type == WindowManager.TRANSIT_TO_BACK -> ExitReason.TASK_MOVED_TO_BACK
- transitionInfo.type == WindowManager.TRANSIT_CLOSE -> ExitReason.TASK_FINISHED
- transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG -> ExitReason.DRAG_TO_EXIT
- transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON ->
+ transitionInfo?.type == WindowManager.TRANSIT_TO_BACK -> ExitReason.TASK_MOVED_TO_BACK
+ transitionInfo?.type == WindowManager.TRANSIT_CLOSE -> ExitReason.TASK_FINISHED
+ transitionInfo?.type == TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG -> ExitReason.DRAG_TO_EXIT
+ transitionInfo?.type == TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON ->
ExitReason.APP_HANDLE_MENU_BUTTON_EXIT
- transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT ->
+ transitionInfo?.type == TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT ->
ExitReason.KEYBOARD_SHORTCUT_EXIT
- transitionInfo.isExitToRecentsTransition() -> ExitReason.RETURN_HOME_OR_OVERVIEW
- transitionInfo.type == Transitions.TRANSIT_MINIMIZE -> ExitReason.TASK_MINIMIZED
+ transitionInfo?.isExitToRecentsTransition() == true ->
+ ExitReason.RETURN_HOME_OR_OVERVIEW
+ transitionInfo?.type == Transitions.TRANSIT_MINIMIZE -> ExitReason.TASK_MINIMIZED
else -> {
ProtoLog.w(
WM_SHELL_DESKTOP_MODE,
"Unknown exit reason for transition type: %s",
- transitionInfo.type,
+ transitionInfo?.type,
)
ExitReason.UNKNOWN_EXIT
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
index 9b9988457808..164d04bbde65 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
@@ -110,8 +110,8 @@ class DesktopModeShellCommandHandler(private val controller: DesktopTasksControl
pw.println("Error: display id should be an integer")
return false
}
- pw.println("Not implemented.")
- return false
+ controller.createDesk(displayId)
+ return true
}
private fun runActivateDesk(args: Array<String>, pw: PrintWriter): Boolean {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index fa696682de28..6636770895fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -171,6 +171,9 @@ class DesktopRepository(
/** Returns a list of all [Desk]s in the repository. */
private fun desksSequence(): Sequence<Desk> = desktopData.desksSequence()
+ /** Returns the number of desks in the given display. */
+ fun getNumberOfDesks(displayId: Int) = desktopData.getNumberOfDesks(displayId)
+
/** Adds [regionListener] to inform about changes to exclusion regions for all Desktop tasks. */
fun setExclusionRegionListener(regionListener: Consumer<Region>, executor: Executor) {
desktopGestureExclusionListener = regionListener
@@ -201,11 +204,11 @@ class DesktopRepository(
/** Adds the given desk under the given display. */
fun addDesk(displayId: Int, deskId: Int) {
- desktopData.getOrCreateDesk(displayId, deskId)
+ desktopData.createDesk(displayId, deskId)
}
/** Returns the default desk in the given display. */
- fun getDefaultDesk(displayId: Int): Int? = desktopData.getDefaultDesk(displayId)?.deskId
+ private fun getDefaultDesk(displayId: Int): Desk? = desktopData.getDefaultDesk(displayId)
/** Sets the given desk as the active one in the given display. */
fun setActiveDesk(displayId: Int, deskId: Int) {
@@ -229,15 +232,14 @@ class DesktopRepository(
* TODO: b/389960283 - add explicit [deskId] argument.
*/
private fun addActiveTask(displayId: Int, taskId: Int) {
- val activeDeskId =
- desktopData.getActiveDesk(displayId)?.deskId
- ?: error("Expected active desk in display: $displayId")
+ val activeDesk = desktopData.getDefaultDesk(displayId)
+ checkNotNull(activeDesk) { "Expected desk in display: $displayId" }
// Removes task if it is active on another desk excluding [activeDesk].
- removeActiveTask(taskId, excludedDeskId = activeDeskId)
+ removeActiveTask(taskId, excludedDeskId = activeDesk.deskId)
- if (desktopData.getOrCreateDesk(displayId, activeDeskId).activeTasks.add(taskId)) {
- logD("Adds active task=%d displayId=%d deskId=%d", taskId, displayId, activeDeskId)
+ if (activeDesk.activeTasks.add(taskId)) {
+ logD("Adds active task=%d displayId=%d deskId=%d", taskId, displayId, activeDesk.deskId)
updateActiveTasksListeners(displayId)
}
}
@@ -266,18 +268,23 @@ class DesktopRepository(
* TODO: b/389960283 - add explicit [deskId] argument.
*/
fun addClosingTask(displayId: Int, taskId: Int) {
- val activeDeskId =
- desktopData.getActiveDesk(displayId)?.deskId
+ val activeDesk =
+ desktopData.getActiveDesk(displayId)
?: error("Expected active desk in display: $displayId")
- if (desktopData.getOrCreateDesk(displayId, activeDeskId).closingTasks.add(taskId)) {
- logD("Added closing task=%d displayId=%d deskId=%d", taskId, displayId, activeDeskId)
+ if (activeDesk.closingTasks.add(taskId)) {
+ logD(
+ "Added closing task=%d displayId=%d deskId=%d",
+ taskId,
+ displayId,
+ activeDesk.deskId,
+ )
} else {
// If the task hasn't been removed from closing list after it disappeared.
logW(
"Task with taskId=%d displayId=%d deskId=%d is already closing",
taskId,
displayId,
- activeDeskId,
+ activeDesk.deskId,
)
}
}
@@ -323,7 +330,7 @@ class DesktopRepository(
/**
* Returns the active tasks in the given display's active desk.
*
- * TODO: b/389960283 - add explicit [deskId] argument.
+ * TODO: b/389960283 - migrate callers to [getActiveTaskIdsInDesk].
*/
@VisibleForTesting
fun getActiveTasks(displayId: Int): ArraySet<Int> =
@@ -332,19 +339,27 @@ class DesktopRepository(
/**
* Returns the minimized tasks in the given display's active desk.
*
- * TODO: b/389960283 - add explicit [deskId] argument.
+ * TODO: b/389960283 - migrate callers to [getMinimizedTaskIdsInDesk].
*/
fun getMinimizedTasks(displayId: Int): ArraySet<Int> =
ArraySet(desktopData.getActiveDesk(displayId)?.minimizedTasks)
+ @VisibleForTesting
+ fun getMinimizedTaskIdsInDesk(deskId: Int): ArraySet<Int> =
+ ArraySet(desktopData.getDesk(deskId)?.minimizedTasks)
+
/**
* Returns all active non-minimized tasks for [displayId] ordered from top to bottom.
*
- * TODO: b/389960283 - add explicit [deskId] argument.
+ * TODO: b/389960283 - migrate callers to [getExpandedTasksIdsInDeskOrdered].
*/
fun getExpandedTasksOrdered(displayId: Int): List<Int> =
getFreeformTasksInZOrder(displayId).filter { !isMinimizedTask(it) }
+ @VisibleForTesting
+ fun getExpandedTasksIdsInDeskOrdered(deskId: Int): List<Int> =
+ getFreeformTasksIdsInDeskInZOrder(deskId).filter { !isMinimizedTask(it) }
+
/**
* Returns the count of active non-minimized tasks for [displayId].
*
@@ -357,11 +372,15 @@ class DesktopRepository(
/**
* Returns a list of freeform tasks, ordered from top-bottom (top at index 0).
*
- * TODO: b/389960283 - add explicit [deskId] argument.
+ * TODO: b/389960283 - migrate callers to [getFreeformTasksIdsInDeskInZOrder].
*/
@VisibleForTesting
fun getFreeformTasksInZOrder(displayId: Int): ArrayList<Int> =
- ArrayList(desktopData.getActiveDesk(displayId)?.freeformTasksInZOrder ?: emptyList())
+ ArrayList(desktopData.getDefaultDesk(displayId)?.freeformTasksInZOrder ?: emptyList())
+
+ @VisibleForTesting
+ fun getFreeformTasksIdsInDeskInZOrder(deskId: Int): ArrayList<Int> =
+ ArrayList(desktopData.getDesk(deskId)?.freeformTasksInZOrder ?: emptyList())
/** Returns the tasks inside the given desk. */
fun getActiveTaskIdsInDesk(deskId: Int): Set<Int> =
@@ -401,8 +420,8 @@ class DesktopRepository(
}
val prevCount = getVisibleTaskCount(displayId)
if (isVisible) {
- desktopData.getActiveDesk(displayId)?.visibleTasks?.add(taskId)
- ?: error("Expected non-null active desk in display $displayId")
+ desktopData.getDefaultDesk(displayId)?.visibleTasks?.add(taskId)
+ ?: error("Expected non-null desk in display $displayId")
unminimizeTask(displayId, taskId)
} else {
desktopData.getActiveDesk(displayId)?.visibleTasks?.remove(taskId)
@@ -587,17 +606,15 @@ class DesktopRepository(
* TODO: b/389960283 - add explicit [deskId] argument.
*/
private fun addOrMoveFreeformTaskToTop(displayId: Int, taskId: Int) {
- val activeDesk =
- desktopData.getActiveDesk(displayId)
- ?: error("Expected a desk to be active in display: $displayId")
+ val desk = getDefaultDesk(displayId) ?: error("Expected a desk in display: $displayId")
logD(
"Add or move task to top: display=%d taskId=%d deskId=%d",
taskId,
displayId,
- activeDesk.deskId,
+ desk.deskId,
)
- desktopData.forAllDesks { _, desk -> desk.freeformTasksInZOrder.remove(taskId) }
- activeDesk.freeformTasksInZOrder.add(0, taskId)
+ desktopData.forAllDesks { _, desk1 -> desk1.freeformTasksInZOrder.remove(taskId) }
+ desk.freeformTasksInZOrder.add(0, taskId)
// Unminimize the task if it is minimized.
unminimizeTask(displayId, taskId)
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
@@ -835,13 +852,8 @@ class DesktopRepository(
/** An interface for the desktop hierarchy's data managed by this repository. */
private interface DesktopData {
- /**
- * Returns the existing desk or creates a new entry if needed.
- *
- * TODO: 389787966 - consider removing this as it cannot be assumed a desk can be created in
- * all devices / form-factors.
- */
- fun getOrCreateDesk(displayId: Int, deskId: Int): Desk
+ /** Creates a desk record. */
+ fun createDesk(displayId: Int, deskId: Int)
/** Returns the desk with the given id, or null if it does not exist. */
fun getDesk(deskId: Int): Desk?
@@ -894,7 +906,8 @@ class DesktopRepository(
/**
* A [DesktopData] implementation that only supports one desk per display.
*
- * Internally, it reuses the displayId as that display's single desk's id.
+ * Internally, it reuses the displayId as that display's single desk's id. It also never truly
+ * "removes" a desk, it just clears its content.
*/
private class SingleDesktopData : DesktopData {
private val deskByDisplayId =
@@ -907,12 +920,13 @@ class DesktopRepository(
}
}
- override fun getOrCreateDesk(displayId: Int, deskId: Int): Desk {
- check(displayId == deskId)
- return deskByDisplayId.getOrCreate(displayId)
+ override fun createDesk(displayId: Int, deskId: Int) {
+ check(displayId == deskId) { "Display and desk ids must match" }
+ deskByDisplayId.getOrCreate(displayId)
}
- override fun getDesk(deskId: Int): Desk = getOrCreateDesk(deskId, deskId)
+ override fun getDesk(deskId: Int): Desk =
+ checkNotNull(deskByDisplayId[deskId]) { "Expected desk $deskId to exist" }
override fun getActiveDesk(displayId: Int): Desk {
// TODO: 389787966 - consider migrating to an "active" state instead of checking the
@@ -927,7 +941,7 @@ class DesktopRepository(
// existence of visible desktop windows, among other factors.
}
- override fun getDefaultDesk(displayId: Int): Desk = getOrCreateDesk(displayId, displayId)
+ override fun getDefaultDesk(displayId: Int): Desk = getDesk(deskId = displayId)
override fun getAllActiveDesks(): Set<Desk> =
deskByDisplayId.valueIterator().asSequence().toSet()
@@ -943,7 +957,7 @@ class DesktopRepository(
}
override fun forAllDesks(displayId: Int, consumer: (Desk) -> Unit) {
- consumer(getOrCreateDesk(displayId, displayId))
+ consumer(getDesk(deskId = displayId))
}
override fun desksSequence(): Sequence<Desk> = deskByDisplayId.valueIterator().asSequence()
@@ -962,16 +976,14 @@ class DesktopRepository(
private class MultiDesktopData : DesktopData {
private val desktopDisplays = SparseArray<DesktopDisplay>()
- override fun getOrCreateDesk(displayId: Int, deskId: Int): Desk {
+ override fun createDesk(displayId: Int, deskId: Int) {
val display =
desktopDisplays[displayId]
?: DesktopDisplay(displayId).also { desktopDisplays[displayId] = it }
- val desk =
- display.orderedDesks.find { desk -> desk.deskId == deskId }
- ?: Desk(deskId = deskId, displayId = displayId).also {
- display.orderedDesks.add(it)
- }
- return desk
+ check(display.orderedDesks.none { desk -> desk.deskId == deskId }) {
+ "Attempting to create desk#$deskId that already exists in display#$displayId"
+ }
+ display.orderedDesks.add(Desk(deskId = deskId, displayId = displayId))
}
override fun getDesk(deskId: Int): Desk? {
@@ -999,7 +1011,8 @@ class DesktopRepository(
override fun getDefaultDesk(displayId: Int): Desk? {
val display = desktopDisplays[displayId] ?: return null
- return display.orderedDesks.firstOrNull()
+ return display.orderedDesks.find { it.deskId == display.activeDeskId }
+ ?: display.orderedDesks.firstOrNull()
}
override fun getAllActiveDesks(): Set<Desk> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
index c958a0975f11..4d87b2189115 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
@@ -70,8 +70,7 @@ class DesktopTaskChangeListener(private val desktopUserRepositories: DesktopUser
if (!isFreeformTask(taskInfo)) {
desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
}
- // TODO: b/367268953 - Connect this with DesktopRepository for handling
- // task moving to front for tasks in windowing mode.
+ desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
}
override fun onTaskMovingToBack(taskInfo: RunningTaskInfo) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 2b4b27046ac3..c63b5ed56f3e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -85,6 +85,7 @@ import com.android.wm.shell.common.RemoteCallable
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SingleInstanceRemoteListener
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.common.UserProfileContexts
import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
import com.android.wm.shell.compatui.isTransparentTask
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
@@ -102,6 +103,8 @@ import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCR
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
+import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
@@ -179,6 +182,9 @@ class DesktopTasksController(
private val desktopTilingDecorViewModel: DesktopTilingDecorViewModel,
private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
private val bubbleController: Optional<BubbleController>,
+ private val overviewToDesktopTransitionObserver: OverviewToDesktopTransitionObserver,
+ private val desksOrganizer: DesksOrganizer,
+ private val userProfileContexts: UserProfileContexts,
) :
RemoteCallable<DesktopTasksController>,
Transitions.TransitionHandler,
@@ -231,6 +237,9 @@ class DesktopTasksController(
// Used to prevent handleRequest from moving the new fullscreen task to freeform.
private var dragAndDropFullscreenCookie: Binder? = null
+ // A listener that is invoked after a desk has been remove from the system. */
+ var onDeskRemovedListener: OnDeskRemovedListener? = null
+
init {
desktopMode = DesktopModeImpl()
if (DesktopModeStatus.canEnterDesktopMode(context)) {
@@ -414,18 +423,38 @@ class DesktopTasksController(
return isFreeformDisplay
}
+ /** Creates a new desk in the given display. */
+ fun createDesk(displayId: Int) {
+ if (Flags.enableMultipleDesktopsBackend()) {
+ desksOrganizer.createDesk(displayId) { deskId ->
+ taskRepository.addDesk(displayId = displayId, deskId = deskId)
+ }
+ } else {
+ // In single-desk, the desk reuses the display id.
+ taskRepository.addDesk(displayId = displayId, deskId = displayId)
+ }
+ }
+
/** Moves task to desktop mode if task is running, else launches it in desktop mode. */
+ @JvmOverloads
fun moveTaskToDesktop(
taskId: Int,
wct: WindowContainerTransaction = WindowContainerTransaction(),
transitionSource: DesktopModeTransitionSource,
remoteTransition: RemoteTransition? = null,
+ callback: IMoveToDesktopCallback? = null,
): Boolean {
val runningTask = shellTaskOrganizer.getRunningTaskInfo(taskId)
if (runningTask == null) {
- return moveBackgroundTaskToDesktop(taskId, wct, transitionSource, remoteTransition)
+ return moveBackgroundTaskToDesktop(
+ taskId,
+ wct,
+ transitionSource,
+ remoteTransition,
+ callback,
+ )
}
- moveRunningTaskToDesktop(runningTask, wct, transitionSource, remoteTransition)
+ moveRunningTaskToDesktop(runningTask, wct, transitionSource, remoteTransition, callback)
return true
}
@@ -434,6 +463,7 @@ class DesktopTasksController(
wct: WindowContainerTransaction,
transitionSource: DesktopModeTransitionSource,
remoteTransition: RemoteTransition? = null,
+ callback: IMoveToDesktopCallback? = null,
): Boolean {
if (recentTasksController?.findTaskInBackground(taskId) == null) {
logW("moveBackgroundTaskToDesktop taskId=%d not found", taskId)
@@ -466,6 +496,7 @@ class DesktopTasksController(
} else {
// TODO(343149901): Add DPI changes for task launch
transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
+ invokeCallbackToOverview(transition, callback)
}
desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
FREEFORM_ANIMATION_DURATION
@@ -483,6 +514,7 @@ class DesktopTasksController(
wct: WindowContainerTransaction = WindowContainerTransaction(),
transitionSource: DesktopModeTransitionSource,
remoteTransition: RemoteTransition? = null,
+ callback: IMoveToDesktopCallback? = null,
) {
if (
DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue() &&
@@ -514,6 +546,7 @@ class DesktopTasksController(
remoteTransitionHandler.setTransition(transition)
} else {
transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
+ invokeCallbackToOverview(transition, callback)
}
desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
FREEFORM_ANIMATION_DURATION
@@ -524,6 +557,15 @@ class DesktopTasksController(
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
}
+ private fun invokeCallbackToOverview(transition: IBinder, callback: IMoveToDesktopCallback?) {
+ // TODO: b/333524374 - Remove this later.
+ // This is a temporary implementation for adding CUJ end and
+ // should be removed when animation is moved to launcher through remote transition.
+ if (callback != null) {
+ overviewToDesktopTransitionObserver.addPendingOverviewTransition(transition, callback)
+ }
+ }
+
/**
* The first part of the animated drag to desktop transition. This is followed with a call to
* [finalizeDragToDesktop] or [cancelDragToDesktop].
@@ -1446,6 +1488,7 @@ class DesktopTasksController(
}
private fun addLaunchHomePendingIntent(wct: WindowContainerTransaction, displayId: Int) {
+ val userHandle = UserHandle.of(userId)
val launchHomeIntent =
Intent(Intent.ACTION_MAIN).apply {
if (displayId != DEFAULT_DISPLAY) {
@@ -1461,11 +1504,13 @@ class DesktopTasksController(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
}
val pendingIntent =
- PendingIntent.getActivity(
+ PendingIntent.getActivityAsUser(
context,
- /* requestCode = */ 0,
+ /* requestCode= */ 0,
launchHomeIntent,
PendingIntent.FLAG_IMMUTABLE,
+ /* options= */ null,
+ userHandle,
)
wct.sendPendingIntent(pendingIntent, launchHomeIntent, options.toBundle())
}
@@ -1819,7 +1864,9 @@ class DesktopTasksController(
// need updates in some cases.
val baseActivity = callingTaskInfo.baseActivity ?: return
val fillIn: Intent =
- context.packageManager.getLaunchIntentForPackage(baseActivity.packageName) ?: return
+ userProfileContexts[callingTaskInfo.userId]
+ ?.packageManager
+ ?.getLaunchIntentForPackage(baseActivity.packageName) ?: return
fillIn.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
val launchIntent =
PendingIntent.getActivity(
@@ -2967,6 +3014,14 @@ class DesktopTasksController(
controller = null
}
+ override fun createDesk(displayId: Int) {
+ // TODO: b/362720497 - Implement this API.
+ }
+
+ override fun activateDesk(deskId: Int, remoteTransition: RemoteTransition?) {
+ // TODO: b/362720497 - Implement this API.
+ }
+
override fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition?) {
executeRemoteCallWithTaskPermission(controller, "showDesktopApps") { c ->
c.showDesktopApps(displayId, remoteTransition)
@@ -2994,17 +3049,6 @@ class DesktopTasksController(
)
}
- override fun getVisibleTaskCount(displayId: Int): Int {
- val result = IntArray(1)
- executeRemoteCallWithTaskPermission(
- controller,
- "visibleTaskCount",
- { controller -> result[0] = controller.visibleTaskCount(displayId) },
- /* blocking= */ true,
- )
- return result[0]
- }
-
override fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo) {
executeRemoteCallWithTaskPermission(controller, "onDesktopSplitSelectAnimComplete") { c
->
@@ -3023,12 +3067,14 @@ class DesktopTasksController(
taskId: Int,
transitionSource: DesktopModeTransitionSource,
remoteTransition: RemoteTransition?,
+ callback: IMoveToDesktopCallback?,
) {
executeRemoteCallWithTaskPermission(controller, "moveTaskToDesktop") { c ->
c.moveTaskToDesktop(
taskId,
transitionSource = transitionSource,
remoteTransition = remoteTransition,
+ callback = callback,
)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index ae4c2773215b..44f7e16e98c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -23,11 +23,17 @@ import android.window.RemoteTransition;
import com.android.wm.shell.desktopmode.IDesktopTaskListener;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.shared.desktopmode.DesktopTaskToFrontReason;
+import com.android.wm.shell.desktopmode.IMoveToDesktopCallback;
/**
* Interface that is exposed to remote callers to manipulate desktop mode features.
*/
interface IDesktopMode {
+ /** If possible, creates a new desk on the display whose ID is `displayId`. */
+ oneway void createDesk(int displayId);
+
+ /** Activates the desk whose ID is `deskId` on whatever display it currently exists on. */
+ oneway void activateDesk(int deskId, in RemoteTransition remoteTransition);
/** Show apps on the desktop on the given display */
void showDesktopApps(int displayId, in RemoteTransition remoteTransition);
@@ -47,9 +53,6 @@ interface IDesktopMode {
oneway void showDesktopApp(int taskId, in @nullable RemoteTransition remoteTransition,
in DesktopTaskToFrontReason toFrontReason);
- /** Get count of visible desktop tasks on the given display */
- int getVisibleTaskCount(int displayId);
-
/** Perform cleanup transactions after the animation to split select is complete */
oneway void onDesktopSplitSelectAnimComplete(in RunningTaskInfo taskInfo);
@@ -58,7 +61,8 @@ interface IDesktopMode {
/** Move a task with given `taskId` to desktop */
void moveToDesktop(int taskId, in DesktopModeTransitionSource transitionSource,
- in @nullable RemoteTransition remoteTransition);
+ in @nullable RemoteTransition remoteTransition,
+ in @nullable IMoveToDesktopCallback callback);
/** Remove desktop on the given display */
oneway void removeDesktop(int displayId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IMoveToDesktopCallback.aidl
index fc51c754e06b..6342528f9cc7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IMoveToDesktopCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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.automotive;
+package com.android.wm.shell.desktopmode;
-import com.android.wm.shell.dagger.WMSingleton;
+interface IMoveToDesktopCallback {
-import dagger.Binds;
-import dagger.Module;
-
-
-@Module
-public abstract class AutoShellModule {
- @WMSingleton
- @Binds
- abstract AutoTaskStackController provideTaskStackController(AutoTaskStackControllerImpl impl);
-}
+ void onTaskMovedToDesktop();
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OverviewToDesktopTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OverviewToDesktopTransitionObserver.kt
new file mode 100644
index 000000000000..873f98389723
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OverviewToDesktopTransitionObserver.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.desktopmode
+
+import android.os.IBinder
+import android.os.RemoteException
+import android.util.Slog
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+
+/** Callback to Launcher overview to notify that add to desktop from overview menu is completed. */
+class OverviewToDesktopTransitionObserver(
+ private val transitions: Transitions,
+ shellInit: ShellInit,
+) : Transitions.TransitionObserver {
+
+ private val transitionToCallback = mutableMapOf<IBinder?, IMoveToDesktopCallback?>()
+
+ init {
+ shellInit.addInitCallback(::onInit, this)
+ }
+
+ fun onInit() {
+ transitions.registerObserver(this)
+ }
+
+ override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
+ try {
+ transitionToCallback[transition]?.onTaskMovedToDesktop()
+ transitionToCallback.clear()
+ } catch (e: RemoteException) {
+ Slog.e(TAG, "onTransitionFinished: Error calling onTaskMovedToDesktop", e)
+ }
+ }
+
+ fun addPendingOverviewTransition(transition: IBinder?, callback: IMoveToDesktopCallback?) {
+ transitionToCallback += transition to callback
+ }
+
+ companion object {
+ const val TAG = "OverviewToDesktopTransitionObserver"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt
index a428ce18a49e..2a1f524bd5d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.desktopmode.compatui
import android.animation.ValueAnimator
+import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.os.IBinder
import android.view.Display.DEFAULT_DISPLAY
@@ -30,6 +31,7 @@ import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
import com.android.wm.shell.desktopmode.DesktopUserRepositories
+import com.android.wm.shell.desktopmode.DesktopWallpaperActivity
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil.isClosingMode
import com.android.wm.shell.shared.TransitionUtil.isClosingType
@@ -128,7 +130,7 @@ class SystemModalsTransitionHandler(
return@find false
}
val taskInfo = change.taskInfo ?: return@find false
- return@find isTopActivityExemptFromDesktopWindowing(context, taskInfo)
+ return@find isSystemModal(context, taskInfo)
}
private fun getClosingSystemModal(info: TransitionInfo): TransitionInfo.Change? =
@@ -137,10 +139,14 @@ class SystemModalsTransitionHandler(
return@find false
}
val taskInfo = change.taskInfo ?: return@find false
- return@find isTopActivityExemptFromDesktopWindowing(context, taskInfo) ||
+ return@find isSystemModal(context, taskInfo) ||
showingSystemModalsIds.contains(taskInfo.taskId)
}
+ private fun isSystemModal(context: Context, taskInfo: RunningTaskInfo): Boolean =
+ !DesktopWallpaperActivity.isWallpaperTask(taskInfo) &&
+ isTopActivityExemptFromDesktopWindowing(context, taskInfo)
+
private fun createAlphaAnimator(
transaction: SurfaceControl.Transaction,
leash: SurfaceControl,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
index 5757c6afd196..b614b3f4d025 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
@@ -22,7 +22,6 @@ import android.content.Context
import android.content.res.Resources
import android.graphics.Point
import android.os.SystemProperties
-import android.util.Slog
import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.desktopmode.CaptionState
@@ -32,27 +31,17 @@ import com.android.wm.shell.shared.annotations.ShellBackgroundThread
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopMode
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
-import com.android.wm.shell.windowdecor.common.DecorThemeUtil
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipColorScheme
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipEducationViewConfig
-import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainCoroutineDispatcher
-import kotlinx.coroutines.TimeoutCancellationException
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.take
-import kotlinx.coroutines.flow.timeout
import kotlinx.coroutines.launch
/**
@@ -72,59 +61,75 @@ class AppHandleEducationController(
@ShellMainThread private val applicationCoroutineScope: CoroutineScope,
@ShellBackgroundThread private val backgroundDispatcher: MainCoroutineDispatcher,
) {
- private val decorThemeUtil = DecorThemeUtil(context)
private lateinit var openHandleMenuCallback: (Int) -> Unit
private lateinit var toDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit
+ private val onTertiaryFixedColor =
+ context.getColor(com.android.internal.R.color.materialColorOnTertiaryFixed)
+ private val tertiaryFixedColor =
+ context.getColor(com.android.internal.R.color.materialColorTertiaryFixed)
init {
runIfEducationFeatureEnabled {
+ // Coroutine block for the first hint that appears on a full-screen app's app handle to
+ // encourage users to open the app handle menu.
applicationCoroutineScope.launch {
- // Central block handling the app handle's educational flow end-to-end.
- isAppHandleHintViewedFlow()
- .flatMapLatest { isAppHandleHintViewed ->
- if (isAppHandleHintViewed) {
- // If the education is viewed then return emptyFlow() that completes
- // immediately.
- // This will help us to not listen to [captionHandleStateFlow] after the
- // education
- // has been viewed already.
- emptyFlow()
- } else {
- // Listen for changes to window decor's caption handle.
- windowDecorCaptionHandleRepository.captionStateFlow
- // Wait for few seconds before emitting the latest state.
- .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS)
- .filter { captionState ->
- captionState is CaptionState.AppHandle &&
- appHandleEducationFilter.shouldShowAppHandleEducation(
- captionState
- )
- }
- }
+ if (isAppHandleHintViewed()) return@launch
+ windowDecorCaptionHandleRepository.captionStateFlow
+ .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS)
+ .filter { captionState ->
+ captionState is CaptionState.AppHandle &&
+ !captionState.isHandleMenuExpanded &&
+ !isAppHandleHintViewed() &&
+ appHandleEducationFilter.shouldShowDesktopModeEducation(captionState)
}
+ .take(1)
.flowOn(backgroundDispatcher)
.collectLatest { captionState ->
- val tooltipColorScheme = tooltipColorScheme(captionState)
-
- showEducation(captionState, tooltipColorScheme)
- // After showing first tooltip, mark education as viewed
+ showEducation(captionState)
appHandleEducationDatastoreRepository
.updateAppHandleHintViewedTimestampMillis(true)
}
}
+ // Coroutine block for the hint that appears when an app handle is expanded to
+ // encourage users to enter desktop mode.
applicationCoroutineScope.launch {
- if (isAppHandleHintUsed()) return@launch
+ if (isEnterDesktopModeHintViewed()) return@launch
windowDecorCaptionHandleRepository.captionStateFlow
+ .debounce(ENTER_DESKTOP_MODE_EDUCATION_DELAY_MILLIS)
.filter { captionState ->
- captionState is CaptionState.AppHandle && captionState.isHandleMenuExpanded
+ captionState is CaptionState.AppHandle &&
+ captionState.isHandleMenuExpanded &&
+ !isEnterDesktopModeHintViewed() &&
+ appHandleEducationFilter.shouldShowDesktopModeEducation(captionState)
}
.take(1)
.flowOn(backgroundDispatcher)
- .collect {
- // If user expands app handle, mark user has used the app handle hint
+ .collectLatest { captionState ->
+ showWindowingImageButtonTooltip(captionState as CaptionState.AppHandle)
appHandleEducationDatastoreRepository
- .updateAppHandleHintUsedTimestampMillis(true)
+ .updateEnterDesktopModeHintViewedTimestampMillis(true)
+ }
+ }
+
+ // Coroutine block for the hint that appears on the window app header in freeform mode
+ // to let users know how to exit desktop mode.
+ applicationCoroutineScope.launch {
+ if (isExitDesktopModeHintViewed()) return@launch
+ windowDecorCaptionHandleRepository.captionStateFlow
+ .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS)
+ .filter { captionState ->
+ captionState is CaptionState.AppHeader &&
+ !captionState.isHeaderMenuExpanded &&
+ !isExitDesktopModeHintViewed() &&
+ appHandleEducationFilter.shouldShowDesktopModeEducation(captionState)
+ }
+ .take(1)
+ .flowOn(backgroundDispatcher)
+ .collectLatest { captionState ->
+ showExitWindowingTooltip(captionState as CaptionState.AppHeader)
+ appHandleEducationDatastoreRepository
+ .updateExitDesktopModeHintViewedTimestampMillis(true)
}
}
}
@@ -135,7 +140,7 @@ class AppHandleEducationController(
block()
}
- private fun showEducation(captionState: CaptionState, tooltipColorScheme: TooltipColorScheme) {
+ private fun showEducation(captionState: CaptionState) {
val appHandleBounds = (captionState as CaptionState.AppHandle).globalAppHandleBounds
val tooltipGlobalCoordinates =
Point(appHandleBounds.left + appHandleBounds.width() / 2, appHandleBounds.bottom)
@@ -145,21 +150,21 @@ class AppHandleEducationController(
val appHandleTooltipConfig =
TooltipEducationViewConfig(
tooltipViewLayout = R.layout.desktop_windowing_education_top_arrow_tooltip,
- tooltipColorScheme = tooltipColorScheme,
+ tooltipColorScheme =
+ TooltipColorScheme(
+ tertiaryFixedColor,
+ onTertiaryFixedColor,
+ onTertiaryFixedColor,
+ ),
tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
tooltipText = getString(R.string.windowing_app_handle_education_tooltip),
arrowDirection =
DesktopWindowingEducationTooltipController.TooltipArrowDirection.UP,
onEducationClickAction = {
- launchWithExceptionHandling {
- showWindowingImageButtonTooltip(tooltipColorScheme)
- }
openHandleMenuCallback(captionState.runningTaskInfo.taskId)
},
onDismissAction = {
- launchWithExceptionHandling {
- showWindowingImageButtonTooltip(tooltipColorScheme)
- }
+ // TODO: b/341320146 - Log previous tooltip was dismissed
},
)
@@ -170,7 +175,7 @@ class AppHandleEducationController(
}
/** Show tooltip that points to windowing image button in app handle menu */
- private suspend fun showWindowingImageButtonTooltip(tooltipColorScheme: TooltipColorScheme) {
+ private suspend fun showWindowingImageButtonTooltip(captionState: CaptionState.AppHandle) {
val appInfoPillHeight = getSize(R.dimen.desktop_mode_handle_menu_app_info_pill_height)
val windowingOptionPillHeight =
getSize(R.dimen.desktop_mode_handle_menu_windowing_pill_height)
@@ -181,128 +186,81 @@ class AppHandleEducationController(
getSize(R.dimen.desktop_mode_handle_menu_margin_top) +
getSize(R.dimen.desktop_mode_handle_menu_pill_spacing_margin)
- windowDecorCaptionHandleRepository.captionStateFlow
- // After the first tooltip was dismissed, wait for 400 ms and see if the app handle menu
- // has been expanded.
- .timeout(APP_HANDLE_EDUCATION_TIMEOUT_MILLIS.milliseconds)
- .catchTimeoutAndLog {
- // TODO: b/341320146 - Log previous tooltip was dismissed
- }
- // Wait for few milliseconds before emitting the latest state.
- .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS)
- .filter { captionState ->
- // Filter out states when app handle is not visible or not expanded.
- captionState is CaptionState.AppHandle && captionState.isHandleMenuExpanded
- }
- // Before showing this tooltip, stop listening to further emissions to avoid
- // accidentally
- // showing the same tooltip on future emissions.
- .take(1)
- .flowOn(backgroundDispatcher)
- .collectLatest { captionState ->
- captionState as CaptionState.AppHandle
- val appHandleBounds = captionState.globalAppHandleBounds
- val tooltipGlobalCoordinates =
- Point(
- appHandleBounds.left + appHandleBounds.width() / 2 + appHandleMenuWidth / 2,
- appHandleBounds.top +
- appHandleMenuMargins +
- appInfoPillHeight +
- windowingOptionPillHeight / 2,
- )
- // Populate information important to inflate windowing image button education
- // tooltip.
- val windowingImageButtonTooltipConfig =
- TooltipEducationViewConfig(
- tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip,
- tooltipColorScheme = tooltipColorScheme,
- tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
- tooltipText =
- getString(
- R.string.windowing_desktop_mode_image_button_education_tooltip
- ),
- arrowDirection =
- DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT,
- onEducationClickAction = {
- launchWithExceptionHandling {
- showExitWindowingTooltip(tooltipColorScheme)
- }
- toDesktopModeCallback(
- captionState.runningTaskInfo.taskId,
- DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON,
- )
- },
- onDismissAction = {
- launchWithExceptionHandling {
- showExitWindowingTooltip(tooltipColorScheme)
- }
- },
+ val appHandleBounds = captionState.globalAppHandleBounds
+ val tooltipGlobalCoordinates =
+ Point(
+ appHandleBounds.left + appHandleBounds.width() / 2 + appHandleMenuWidth / 2,
+ appHandleBounds.top +
+ appHandleMenuMargins +
+ appInfoPillHeight +
+ windowingOptionPillHeight / 2,
+ )
+ // Populate information important to inflate windowing image button education
+ // tooltip.
+ val windowingImageButtonTooltipConfig =
+ TooltipEducationViewConfig(
+ tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip,
+ tooltipColorScheme =
+ TooltipColorScheme(
+ tertiaryFixedColor,
+ onTertiaryFixedColor,
+ onTertiaryFixedColor,
+ ),
+ tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
+ tooltipText =
+ getString(R.string.windowing_desktop_mode_image_button_education_tooltip),
+ arrowDirection =
+ DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT,
+ onEducationClickAction = {
+ toDesktopModeCallback(
+ captionState.runningTaskInfo.taskId,
+ DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON,
)
+ },
+ onDismissAction = {
+ // TODO: b/341320146 - Log previous tooltip was dismissed
+ },
+ )
- windowingEducationViewController.showEducationTooltip(
- taskId = captionState.runningTaskInfo.taskId,
- tooltipViewConfig = windowingImageButtonTooltipConfig,
- )
- }
+ windowingEducationViewController.showEducationTooltip(
+ taskId = captionState.runningTaskInfo.taskId,
+ tooltipViewConfig = windowingImageButtonTooltipConfig,
+ )
}
/** Show tooltip that points to app chip button and educates user on how to exit desktop mode */
- private suspend fun showExitWindowingTooltip(tooltipColorScheme: TooltipColorScheme) {
- windowDecorCaptionHandleRepository.captionStateFlow
- // After the previous tooltip was dismissed, wait for 400 ms and see if the user entered
- // desktop mode.
- .timeout(APP_HANDLE_EDUCATION_TIMEOUT_MILLIS.milliseconds)
- .catchTimeoutAndLog {
- // TODO: b/341320146 - Log previous tooltip was dismissed
- }
- // Wait for few milliseconds before emitting the latest state.
- .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS)
- .filter { captionState ->
- // Filter out states when app header is not visible or expanded.
- captionState is CaptionState.AppHeader && !captionState.isHeaderMenuExpanded
- }
- // Before showing this tooltip, stop listening to further emissions to avoid
- // accidentally
- // showing the same tooltip on future emissions.
- .take(1)
- .flowOn(backgroundDispatcher)
- .collectLatest { captionState ->
- captionState as CaptionState.AppHeader
- val globalAppChipBounds = captionState.globalAppChipBounds
- val tooltipGlobalCoordinates =
- Point(
- globalAppChipBounds.right,
- globalAppChipBounds.top + globalAppChipBounds.height() / 2,
- )
- // Populate information important to inflate exit desktop mode education tooltip.
- val exitWindowingTooltipConfig =
- TooltipEducationViewConfig(
- tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip,
- tooltipColorScheme = tooltipColorScheme,
- tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
- tooltipText =
- getString(R.string.windowing_desktop_mode_exit_education_tooltip),
- arrowDirection =
- DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT,
- onDismissAction = {},
- onEducationClickAction = {
- openHandleMenuCallback(captionState.runningTaskInfo.taskId)
- },
- )
- windowingEducationViewController.showEducationTooltip(
- taskId = captionState.runningTaskInfo.taskId,
- tooltipViewConfig = exitWindowingTooltipConfig,
- )
- }
- }
-
- private fun tooltipColorScheme(captionState: CaptionState): TooltipColorScheme {
- val onTertiaryFixed =
- context.getColor(com.android.internal.R.color.materialColorOnTertiaryFixed)
- val tertiaryFixed =
- context.getColor(com.android.internal.R.color.materialColorTertiaryFixed)
-
- return TooltipColorScheme(tertiaryFixed, onTertiaryFixed, onTertiaryFixed)
+ private suspend fun showExitWindowingTooltip(captionState: CaptionState.AppHeader) {
+ val globalAppChipBounds = captionState.globalAppChipBounds
+ val tooltipGlobalCoordinates =
+ Point(
+ globalAppChipBounds.right,
+ globalAppChipBounds.top + globalAppChipBounds.height() / 2,
+ )
+ // Populate information important to inflate exit desktop mode education tooltip.
+ val exitWindowingTooltipConfig =
+ TooltipEducationViewConfig(
+ tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip,
+ tooltipColorScheme =
+ TooltipColorScheme(
+ tertiaryFixedColor,
+ onTertiaryFixedColor,
+ onTertiaryFixedColor,
+ ),
+ tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
+ tooltipText = getString(R.string.windowing_desktop_mode_exit_education_tooltip),
+ arrowDirection =
+ DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT,
+ onDismissAction = {
+ // TODO: b/341320146 - Log previous tooltip was dismissed
+ },
+ onEducationClickAction = {
+ openHandleMenuCallback(captionState.runningTaskInfo.taskId)
+ },
+ )
+ windowingEducationViewController.showEducationTooltip(
+ taskId = captionState.runningTaskInfo.taskId,
+ tooltipViewConfig = exitWindowingTooltipConfig,
+ )
}
/**
@@ -319,43 +277,20 @@ class AppHandleEducationController(
this.toDesktopModeCallback = toDesktopModeCallback
}
- private inline fun <T> Flow<T>.catchTimeoutAndLog(crossinline block: () -> Unit) =
- catch { exception ->
- if (exception is TimeoutCancellationException) block() else throw exception
- }
-
- private fun launchWithExceptionHandling(block: suspend () -> Unit) =
- applicationCoroutineScope.launch {
- try {
- block()
- } catch (e: Throwable) {
- Slog.e(TAG, "Error: ", e)
- }
- }
+ private suspend fun isAppHandleHintViewed(): Boolean =
+ appHandleEducationDatastoreRepository.dataStoreFlow
+ .first()
+ .hasAppHandleHintViewedTimestampMillis() && !FORCE_SHOW_DESKTOP_MODE_EDUCATION
- /**
- * Listens to the changes to [WindowingEducationProto#hasAppHandleHintViewedTimestampMillis()]
- * in datastore proto object.
- *
- * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this flow will always emit false. That
- * means it will always emit app handle hint has not been viewed yet.
- */
- private fun isAppHandleHintViewedFlow(): Flow<Boolean> =
+ private suspend fun isEnterDesktopModeHintViewed(): Boolean =
appHandleEducationDatastoreRepository.dataStoreFlow
- .map { preferences ->
- preferences.hasAppHandleHintViewedTimestampMillis() &&
- !SHOULD_OVERRIDE_EDUCATION_CONDITIONS
- }
- .distinctUntilChanged()
+ .first()
+ .hasEnterDesktopModeHintViewedTimestampMillis() && !FORCE_SHOW_DESKTOP_MODE_EDUCATION
- /**
- * Listens to the changes to [WindowingEducationProto#hasAppHandleHintUsedTimestampMillis()] in
- * datastore proto object.
- */
- private suspend fun isAppHandleHintUsed(): Boolean =
+ private suspend fun isExitDesktopModeHintViewed(): Boolean =
appHandleEducationDatastoreRepository.dataStoreFlow
.first()
- .hasAppHandleHintUsedTimestampMillis()
+ .hasExitDesktopModeHintViewedTimestampMillis() && !FORCE_SHOW_DESKTOP_MODE_EDUCATION
private fun getSize(@DimenRes resourceId: Int): Int {
if (resourceId == Resources.ID_NULL) return 0
@@ -369,13 +304,17 @@ class AppHandleEducationController(
val APP_HANDLE_EDUCATION_DELAY_MILLIS: Long
get() = SystemProperties.getLong("persist.windowing_app_handle_education_delay", 3000L)
- val APP_HANDLE_EDUCATION_TIMEOUT_MILLIS: Long
- get() = SystemProperties.getLong("persist.windowing_app_handle_education_timeout", 400L)
+ val ENTER_DESKTOP_MODE_EDUCATION_DELAY_MILLIS: Long
+ get() =
+ SystemProperties.getLong(
+ "persist.windowing_enter_desktop_mode_education_timeout",
+ 400L,
+ )
- val SHOULD_OVERRIDE_EDUCATION_CONDITIONS: Boolean
+ val FORCE_SHOW_DESKTOP_MODE_EDUCATION: Boolean
get() =
SystemProperties.getBoolean(
- "persist.desktop_windowing_app_handle_education_override_conditions",
+ "persist.windowing_force_show_desktop_mode_education",
false,
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt
index 9990846fc92e..4d219b5544aa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt
@@ -17,13 +17,14 @@
package com.android.wm.shell.desktopmode.education
import android.annotation.IntegerRes
+import android.app.ActivityManager.RunningTaskInfo
import android.app.usage.UsageStatsManager
import android.content.Context
import android.os.SystemClock
import android.provider.Settings.Secure
import com.android.wm.shell.R
import com.android.wm.shell.desktopmode.CaptionState
-import com.android.wm.shell.desktopmode.education.AppHandleEducationController.Companion.SHOULD_OVERRIDE_EDUCATION_CONDITIONS
+import com.android.wm.shell.desktopmode.education.AppHandleEducationController.Companion.FORCE_SHOW_DESKTOP_MODE_EDUCATION
import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository
import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto
import java.time.Duration
@@ -37,26 +38,28 @@ class AppHandleEducationFilter(
private val usageStatsManager =
context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
+ suspend fun shouldShowDesktopModeEducation(captionState: CaptionState.AppHeader): Boolean =
+ shouldShowDesktopModeEducation(captionState.runningTaskInfo)
+
+ suspend fun shouldShowDesktopModeEducation(captionState: CaptionState.AppHandle): Boolean =
+ shouldShowDesktopModeEducation(captionState.runningTaskInfo)
+
/**
- * Returns true if conditions to show app handle education are met, returns false otherwise.
+ * Returns true if conditions to show app handle, enter desktop mode and exit desktop mode
+ * education are met based on the app info and usage, returns false otherwise.
*
- * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this method will always return
- * ![captionState.isHandleMenuExpanded].
+ * If [FORCE_SHOW_DESKTOP_MODE_EDUCATION] is true, this method will always return true.
*/
- suspend fun shouldShowAppHandleEducation(captionState: CaptionState): Boolean {
- if ((captionState as CaptionState.AppHandle).isHandleMenuExpanded) return false
- if (SHOULD_OVERRIDE_EDUCATION_CONDITIONS) return true
+ private suspend fun shouldShowDesktopModeEducation(taskInfo: RunningTaskInfo): Boolean {
+ if (FORCE_SHOW_DESKTOP_MODE_EDUCATION) return true
- val focusAppPackageName =
- captionState.runningTaskInfo.topActivityInfo?.packageName ?: return false
+ val focusAppPackageName = taskInfo.topActivityInfo?.packageName ?: return false
val windowingEducationProto =
appHandleEducationDatastoreRepository.windowingEducationProto()
return isFocusAppInAllowlist(focusAppPackageName) &&
!isOtherEducationShowing() &&
hasSufficientTimeSinceSetup() &&
- !isAppHandleHintViewedBefore(windowingEducationProto) &&
- !isAppHandleHintUsedBefore(windowingEducationProto) &&
hasMinAppUsage(windowingEducationProto, focusAppPackageName)
}
@@ -79,14 +82,6 @@ class AppHandleEducationFilter(
R.integer.desktop_windowing_education_required_time_since_setup_seconds
)
- private fun isAppHandleHintViewedBefore(
- windowingEducationProto: WindowingEducationProto
- ): Boolean = windowingEducationProto.hasAppHandleHintViewedTimestampMillis()
-
- private fun isAppHandleHintUsedBefore(
- windowingEducationProto: WindowingEducationProto
- ): Boolean = windowingEducationProto.hasAppHandleHintUsedTimestampMillis()
-
private suspend fun hasMinAppUsage(
windowingEducationProto: WindowingEducationProto,
focusAppPackageName: String,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
index 3e120b09a0b6..d061e03b9be5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
@@ -91,6 +91,40 @@ constructor(private val dataStore: DataStore<WindowingEducationProto>) {
}
/**
+ * Updates [WindowingEducationProto.enterDesktopModeHintViewedTimestampMillis_] field in
+ * datastore with current timestamp if [isViewed] is true, if not then clears the field.
+ */
+ suspend fun updateEnterDesktopModeHintViewedTimestampMillis(isViewed: Boolean) {
+ dataStore.updateData { preferences ->
+ if (isViewed) {
+ preferences
+ .toBuilder()
+ .setEnterDesktopModeHintViewedTimestampMillis(System.currentTimeMillis())
+ .build()
+ } else {
+ preferences.toBuilder().clearEnterDesktopModeHintViewedTimestampMillis().build()
+ }
+ }
+ }
+
+ /**
+ * Updates [WindowingEducationProto.exitDesktopModeHintViewedTimestampMillis_] field in
+ * datastore with current timestamp if [isViewed] is true, if not then clears the field.
+ */
+ suspend fun updateExitDesktopModeHintViewedTimestampMillis(isViewed: Boolean) {
+ dataStore.updateData { preferences ->
+ if (isViewed) {
+ preferences
+ .toBuilder()
+ .setExitDesktopModeHintViewedTimestampMillis(System.currentTimeMillis())
+ .build()
+ } else {
+ preferences.toBuilder().clearExitDesktopModeHintViewedTimestampMillis().build()
+ }
+ }
+ }
+
+ /**
* Updates [WindowingEducationProto.appHandleHintUsedTimestampMillis_] field in datastore with
* current timestamp if [isViewed] is true, if not then clears the field.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
new file mode 100644
index 000000000000..5cbb59fbf323
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.desktopmode.multidesks
+
+import android.app.ActivityManager
+import android.window.TransitionInfo
+import android.window.WindowContainerTransaction
+
+/** An organizer of desk containers in which to host child desktop windows. */
+interface DesksOrganizer {
+ /** Creates a new desk container in the given display. */
+ fun createDesk(displayId: Int, callback: OnCreateCallback)
+
+ /** Activates the given desk, making it visible in its display. */
+ fun activateDesk(wct: WindowContainerTransaction, deskId: Int)
+
+ /** Removes the given desk and its desktop windows. */
+ fun removeDesk(wct: WindowContainerTransaction, deskId: Int)
+
+ /** Moves the given task to the given desk. */
+ fun moveTaskToDesk(
+ wct: WindowContainerTransaction,
+ deskId: Int,
+ task: ActivityManager.RunningTaskInfo,
+ )
+
+ /**
+ * Returns the desk id in which the task in the given change is located at the end of a
+ * transition, if any.
+ */
+ fun getDeskAtEnd(change: TransitionInfo.Change): Int?
+
+ /** A callback that is invoked when the desk container is created. */
+ fun interface OnCreateCallback {
+ /** Calls back when the [deskId] has been created. */
+ fun onCreated(deskId: Int)
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/OnDeskRemovedListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/OnDeskRemovedListener.kt
new file mode 100644
index 000000000000..452ddb1ff8fb
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/OnDeskRemovedListener.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.desktopmode.multidesks
+
+/** A listener for removals of desks. */
+fun interface OnDeskRemovedListener {
+ /** Called when a desk has been removed from the system. */
+ fun onDeskRemoved(lastDisplayId: Int, deskId: Int)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
new file mode 100644
index 000000000000..79c48c5e9594
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.desktopmode.multidesks
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.util.SparseArray
+import android.view.SurfaceControl
+import android.window.TransitionInfo
+import android.window.WindowContainerTransaction
+import androidx.core.util.forEach
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.protolog.ProtoLog
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer.OnCreateCallback
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.sysui.ShellCommandHandler
+import com.android.wm.shell.sysui.ShellInit
+import java.io.PrintWriter
+
+/** A [DesksOrganizer] that uses root tasks as the container of each desk. */
+class RootTaskDesksOrganizer(
+ shellInit: ShellInit,
+ shellCommandHandler: ShellCommandHandler,
+ private val shellTaskOrganizer: ShellTaskOrganizer,
+) : DesksOrganizer, ShellTaskOrganizer.TaskListener {
+
+ private val deskCreateRequests = mutableListOf<CreateRequest>()
+ @VisibleForTesting val roots = SparseArray<DeskRoot>()
+
+ init {
+ if (Flags.enableMultipleDesktopsBackend()) {
+ shellInit.addInitCallback(
+ { shellCommandHandler.addDumpCallback(this::dump, this) },
+ this,
+ )
+ }
+ }
+
+ override fun createDesk(displayId: Int, callback: OnCreateCallback) {
+ logV("createDesk in display: %d", displayId)
+ deskCreateRequests += CreateRequest(displayId, callback)
+ shellTaskOrganizer.createRootTask(
+ displayId,
+ WINDOWING_MODE_FREEFORM,
+ /* listener = */ this,
+ /* removeWithTaskOrganizer = */ true,
+ )
+ }
+
+ override fun removeDesk(wct: WindowContainerTransaction, deskId: Int) {
+ logV("removeDesk %d", deskId)
+ val desk = checkNotNull(roots[deskId]) { "Root not found for desk: $deskId" }
+ wct.removeRootTask(desk.taskInfo.token)
+ }
+
+ override fun activateDesk(wct: WindowContainerTransaction, deskId: Int) {
+ logV("activateDesk %d", deskId)
+ val root = checkNotNull(roots[deskId]) { "Root not found for desk: $deskId" }
+ wct.reorder(root.taskInfo.token, /* onTop= */ true)
+ wct.setLaunchRoot(
+ /* container= */ root.taskInfo.token,
+ /* windowingModes= */ intArrayOf(WINDOWING_MODE_FREEFORM, WINDOWING_MODE_UNDEFINED),
+ /* activityTypes= */ intArrayOf(ACTIVITY_TYPE_UNDEFINED, ACTIVITY_TYPE_STANDARD),
+ )
+ }
+
+ override fun moveTaskToDesk(
+ wct: WindowContainerTransaction,
+ deskId: Int,
+ task: RunningTaskInfo,
+ ) {
+ val root = roots[deskId] ?: error("Root not found for desk: $deskId")
+ wct.reparent(task.token, root.taskInfo.token, /* onTop= */ true)
+ }
+
+ override fun getDeskAtEnd(change: TransitionInfo.Change): Int? =
+ change.taskInfo?.parentTaskId?.takeIf { it in roots }
+
+ override fun onTaskAppeared(taskInfo: RunningTaskInfo, leash: SurfaceControl) {
+ if (taskInfo.parentTaskId in roots) {
+ val deskId = taskInfo.parentTaskId
+ val taskId = taskInfo.taskId
+ logV("Task #$taskId appeared in desk #$deskId")
+ addChildToDesk(taskId = taskId, deskId = deskId)
+ return
+ }
+ val deskId = taskInfo.taskId
+ check(deskId !in roots) { "A root already exists for desk: $deskId" }
+ val request =
+ checkNotNull(deskCreateRequests.firstOrNull { it.displayId == taskInfo.displayId }) {
+ "Task ${taskInfo.taskId} appeared without pending create request"
+ }
+ logV("Desk #$deskId appeared")
+ roots[deskId] = DeskRoot(deskId, taskInfo, leash)
+ deskCreateRequests.remove(request)
+ request.onCreateCallback.onCreated(deskId)
+ }
+
+ override fun onTaskInfoChanged(taskInfo: RunningTaskInfo) {
+ if (roots.contains(taskInfo.taskId)) {
+ val deskId = taskInfo.taskId
+ roots[deskId] = roots[deskId].copy(taskInfo = taskInfo)
+ }
+ }
+
+ override fun onTaskVanished(taskInfo: RunningTaskInfo) {
+ if (roots.contains(taskInfo.taskId)) {
+ val deskId = taskInfo.taskId
+ val deskRoot = roots[deskId]
+ // Use the last saved taskInfo to obtain the displayId. Using the local one here will
+ // return -1 since the task is not unassociated with a display.
+ val displayId = deskRoot.taskInfo.displayId
+ logV("Desk #$deskId vanished from display #$displayId")
+ roots.remove(deskId)
+ return
+ }
+ // At this point, [parentTaskId] may be unset even if this is a task vanishing from a desk,
+ // so search through each root to remove this if it's a child.
+ roots.forEach { deskId, deskRoot ->
+ if (deskRoot.children.remove(taskInfo.taskId)) {
+ logV("Task #${taskInfo.taskId} vanished from desk #$deskId")
+ return
+ }
+ }
+ }
+
+ @VisibleForTesting
+ data class DeskRoot(
+ val deskId: Int,
+ val taskInfo: RunningTaskInfo,
+ val leash: SurfaceControl,
+ val children: MutableSet<Int> = mutableSetOf(),
+ )
+
+ override fun dump(pw: PrintWriter, prefix: String) {
+ val innerPrefix = "$prefix "
+ pw.println("$prefix$TAG")
+ pw.println("${innerPrefix}Desk Roots:")
+ roots.forEach { deskId, root ->
+ pw.println("$innerPrefix #$deskId visible=${root.taskInfo.isVisible}")
+ pw.println("$innerPrefix children=${root.children}")
+ }
+ }
+
+ private fun addChildToDesk(taskId: Int, deskId: Int) {
+ roots.forEach { _, deskRoot ->
+ if (deskRoot.deskId == deskId) {
+ deskRoot.children.add(taskId)
+ } else {
+ deskRoot.children.remove(taskId)
+ }
+ }
+ }
+
+ private data class CreateRequest(val displayId: Int, val onCreateCallback: OnCreateCallback)
+
+ private fun logV(msg: String, vararg arguments: Any?) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ companion object {
+ private const val TAG = "RootTaskDesksOrganizer"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
index 58a49a035bb6..5a89451ffdbc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
@@ -18,6 +18,7 @@ package com.android.wm.shell.desktopmode.persistence
import android.content.Context
import android.window.DesktopModeFlags
+import com.android.window.flags.Flags
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.shared.annotations.ShellMainThread
@@ -54,10 +55,22 @@ class DesktopRepositoryInitializerImpl(
DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 }
?: persistentDesktop.zOrderedTasksCount
var visibleTasksCount = 0
+ repository.addDesk(
+ displayId = persistentDesktop.displayId,
+ deskId =
+ if (Flags.enableMultipleDesktopsBackend()) {
+ persistentDesktop.desktopId
+ } else {
+ // When disabled, desk ids are always the display id.
+ persistentDesktop.displayId
+ },
+ )
persistentDesktop.zOrderedTasksList
// Reverse it so we initialize the repo from bottom to top.
.reversed()
.mapNotNull { taskId -> persistentDesktop.tasksByTaskIdMap[taskId] }
+ // TODO: b/362720497 - add tasks to their respective desk when multi-desk
+ // persistence is implemented.
.forEach { task ->
if (
task.desktopTaskState == DesktopTaskState.VISIBLE &&
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 0d5aa0105659..897e2d1601a5 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
@@ -29,6 +29,7 @@ import android.window.DesktopModeFlags;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
@@ -52,6 +53,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
private final ShellTaskOrganizer mShellTaskOrganizer;
private final Optional<DesktopUserRepositories> mDesktopUserRepositories;
private final Optional<DesktopTasksController> mDesktopTasksController;
+ private final DesktopModeLoggerTransitionObserver mDesktopModeLoggerTransitionObserver;
private final WindowDecorViewModel mWindowDecorationViewModel;
private final LaunchAdjacentController mLaunchAdjacentController;
private final Optional<TaskChangeListener> mTaskChangeListener;
@@ -64,6 +66,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
ShellTaskOrganizer shellTaskOrganizer,
Optional<DesktopUserRepositories> desktopUserRepositories,
Optional<DesktopTasksController> desktopTasksController,
+ DesktopModeLoggerTransitionObserver desktopModeLoggerTransitionObserver,
LaunchAdjacentController launchAdjacentController,
WindowDecorViewModel windowDecorationViewModel,
Optional<TaskChangeListener> taskChangeListener) {
@@ -72,6 +75,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
mWindowDecorationViewModel = windowDecorationViewModel;
mDesktopUserRepositories = desktopUserRepositories;
mDesktopTasksController = desktopTasksController;
+ mDesktopModeLoggerTransitionObserver = desktopModeLoggerTransitionObserver;
mLaunchAdjacentController = launchAdjacentController;
mTaskChangeListener = taskChangeListener;
if (shellInit != null) {
@@ -130,6 +134,9 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
repository.removeTask(taskInfo.displayId, taskInfo.taskId);
}
}
+ // TODO: b/367268649 - This listener shouldn't need to call the transition observer directly
+ // for logging once the logic in the observer is moved.
+ mDesktopModeLoggerTransitionObserver.onTaskVanished(taskInfo);
mWindowDecorationViewModel.onTaskVanished(taskInfo);
updateLaunchAdjacentController();
}
@@ -171,7 +178,8 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
@Override
public void onFocusTaskChanged(RunningTaskInfo taskInfo) {
- if (taskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+ if (taskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM
+ || DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue()) {
return;
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG,
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 bec75b3d865c..20b8c5ec45ce 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
@@ -21,6 +21,9 @@ 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.pip.PipTransitionController.ANIM_TYPE_ALPHA;
+import static com.android.wm.shell.pip.PipTransitionController.ANIM_TYPE_BOUNDS;
+
import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.RectEvaluator;
@@ -59,16 +62,6 @@ public class PipAnimationController {
static final float FRACTION_START = 0f;
static final float FRACTION_END = 1f;
- public static final int ANIM_TYPE_BOUNDS = 0;
- public static final int ANIM_TYPE_ALPHA = 1;
-
- @IntDef(prefix = { "ANIM_TYPE_" }, value = {
- ANIM_TYPE_BOUNDS,
- ANIM_TYPE_ALPHA
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface AnimationType {}
-
/**
* The alpha type is set for swiping to home. But the swiped task may not enter PiP. And if
* another task enters PiP by non-swipe ways, e.g. call API in foreground or switch to 3-button
@@ -125,7 +118,7 @@ public class PipAnimationController {
});
private PipTransitionAnimator mCurrentAnimator;
- @AnimationType
+ @PipTransitionController.AnimationType
private int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private long mLastOneShotAlphaAnimationTime;
@@ -157,9 +150,9 @@ public class PipAnimationController {
/**
* Construct and return an animator that animates from the {@param startBounds} to the
* {@param endBounds} with the given {@param direction}. If {@param direction} is type
- * {@link ANIM_TYPE_BOUNDS}, then {@param sourceHintRect} will be used to animate
- * in a better, more smooth manner. If the original bound was rotated and a reset needs to
- * happen, pass in {@param startingAngle}.
+ * {@link PipTransitionController#ANIM_TYPE_BOUNDS}, then {@param sourceHintRect} will be used
+ * to animate in a better, more smooth manner. If the original bound was rotated and a reset
+ * needs to happen, pass in {@param startingAngle}.
*
* In the case where one wants to start animation during an intermediate animation (for example,
* if the user is currently doing a pinch-resize, and upon letting go now PiP needs to animate
@@ -244,12 +237,13 @@ public class PipAnimationController {
/**
* Sets the preferred enter animation type for one time. This is typically used to set the
- * animation type to {@link PipAnimationController#ANIM_TYPE_ALPHA}.
+ * animation type to {@link PipTransitionController#ANIM_TYPE_ALPHA}.
* <p>
* For example, gesture navigation would first fade out the PiP activity, and the transition
* should be responsible to animate in (such as fade in) the PiP.
*/
- public void setOneShotEnterAnimationType(@AnimationType int animationType) {
+ public void setOneShotEnterAnimationType(
+ @PipTransitionController.AnimationType int animationType) {
mOneShotAnimationType = animationType;
if (animationType == ANIM_TYPE_ALPHA) {
mLastOneShotAlphaAnimationTime = SystemClock.uptimeMillis();
@@ -257,7 +251,7 @@ public class PipAnimationController {
}
/** Returns the preferred animation type and consumes the one-shot type if needed. */
- @AnimationType
+ @PipTransitionController.AnimationType
public int takeOneShotEnterAnimationType() {
final int type = mOneShotAnimationType;
if (type == ANIM_TYPE_ALPHA) {
@@ -321,7 +315,7 @@ public class PipAnimationController {
ValueAnimator.AnimatorListener {
private final TaskInfo mTaskInfo;
private final SurfaceControl mLeash;
- private final @AnimationType int mAnimationType;
+ private final @PipTransitionController.AnimationType int mAnimationType;
private final Rect mDestinationBounds = new Rect();
private final Point mLeashOffset = new Point();
@@ -341,16 +335,17 @@ public class PipAnimationController {
private boolean mHasRequestedEnd;
private PipTransitionAnimator(@NonNull TaskInfo taskInfo, @NonNull SurfaceControl leash,
- @AnimationType int animationType, @NonNull Rect destinationBounds,
- @NonNull T baseValue, @NonNull T startValue, @NonNull T endValue) {
+ @PipTransitionController.AnimationType int animationType,
+ @NonNull Rect destinationBounds, @NonNull T baseValue, @NonNull T startValue,
+ @NonNull T endValue) {
this(taskInfo, leash, animationType, destinationBounds, new Point(), baseValue,
startValue, endValue);
}
private PipTransitionAnimator(@NonNull TaskInfo taskInfo, @NonNull SurfaceControl leash,
- @AnimationType int animationType, @NonNull Rect destinationBounds,
- @NonNull Point leashOffset, @NonNull T baseValue, @NonNull T startValue,
- @NonNull T endValue) {
+ @PipTransitionController.AnimationType int animationType,
+ @NonNull Rect destinationBounds, @NonNull Point leashOffset,
+ @NonNull T baseValue, @NonNull T startValue, @NonNull T endValue) {
mTaskInfo = taskInfo;
mLeash = leash;
mAnimationType = animationType;
@@ -408,7 +403,8 @@ public class PipAnimationController {
@Override public void onAnimationRepeat(Animator animation) {}
@VisibleForTesting
- @AnimationType public int getAnimationType() {
+ @PipTransitionController.AnimationType
+ public int getAnimationType() {
return mAnimationType;
}
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 af187682d379..61a193c7d523 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
@@ -28,8 +28,6 @@ import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
import static com.android.wm.shell.desktopmode.DesktopModeUtils.calculateInitialBounds;
import static com.android.wm.shell.desktopmode.DesktopTasksController.DESKTOP_MODE_INITIAL_BOUNDS_SCALE;
-import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
-import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
import static com.android.wm.shell.pip.PipAnimationController.FRACTION_START;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
@@ -43,6 +41,8 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isRemovePipDirection;
+import static com.android.wm.shell.pip.PipTransitionController.ANIM_TYPE_ALPHA;
+import static com.android.wm.shell.pip.PipTransitionController.ANIM_TYPE_BOUNDS;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -1716,7 +1716,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction,
- @PipAnimationController.AnimationType int type) {
+ @PipTransitionController.AnimationType int type) {
final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds());
mPipBoundsState.setBounds(destinationBounds);
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
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 bba778d9f438..2f3c15208621 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
@@ -32,8 +32,6 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
-import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
-import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
@@ -116,7 +114,7 @@ public class PipTransition extends PipTransitionController {
private final HomeTransitionObserver mHomeTransitionObserver;
private final Optional<SplitScreenController> mSplitScreenOptional;
private final PipAnimationController mPipAnimationController;
- private @PipAnimationController.AnimationType int mEnterAnimationType = ANIM_TYPE_BOUNDS;
+ private @AnimationType int mEnterAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
private SurfaceControl.Transaction mFinishTransaction;
private final Rect mExitDestinationBounds = new Rect();
@@ -992,7 +990,7 @@ public class PipTransition extends PipTransitionController {
}
@Override
- public void setEnterAnimationType(@PipAnimationController.AnimationType int type) {
+ public void setEnterAnimationType(@AnimationType int type) {
mEnterAnimationType = type;
}
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 91150b0070ce..da3181096d98 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
@@ -23,6 +23,7 @@ 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;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.Flags;
@@ -55,6 +56,8 @@ import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
@@ -73,6 +76,17 @@ public abstract class PipTransitionController implements Transitions.TransitionH
protected PipTaskOrganizer mPipOrganizer;
protected DefaultMixedHandler mMixedHandler;
+ public static final int ANIM_TYPE_BOUNDS = 0;
+ public static final int ANIM_TYPE_ALPHA = 1;
+
+ @IntDef(prefix = { "ANIM_TYPE_" }, value = {
+ ANIM_TYPE_BOUNDS,
+ ANIM_TYPE_ALPHA
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AnimationType {}
+
+
protected final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
new PipAnimationController.PipAnimationCallback() {
@Override
@@ -357,7 +371,7 @@ public abstract class PipTransitionController implements Transitions.TransitionH
}
/** Sets the type of animation when a PiP task appears. */
- public void setEnterAnimationType(@PipAnimationController.AnimationType int type) {
+ public void setEnterAnimationType(@AnimationType int type) {
}
/** Play a transition animation for entering PiP on a specific PiP change. */
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 912d3839fae7..9cf822b32a74 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
@@ -22,7 +22,6 @@ import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_PIP_TRANSITION;
-import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN;
@@ -32,6 +31,7 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
+import static com.android.wm.shell.pip.PipTransitionController.ANIM_TYPE_ALPHA;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 8cba076d28f2..272cb4372acf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -141,6 +141,8 @@ public class PipTransition extends PipTransitionController implements
private ValueAnimator mTransitionAnimator;
+ private @AnimationType int mEnterAnimationType = ANIM_TYPE_BOUNDS;
+
public PipTransition(
Context context,
@NonNull ShellInit shellInit,
@@ -812,12 +814,11 @@ public class PipTransition extends PipTransitionController implements
if (TransitionUtil.isOpeningType(info.getType())) {
for (TransitionInfo.Change change : info.getChanges()) {
if (change.getLeash() == null) continue;
- if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
+ if (TransitionUtil.isOpeningMode(change.getMode())) {
startTransaction.setAlpha(change.getLeash(), 1f);
}
}
}
-
}
private WindowContainerTransaction getEnterPipTransaction(@NonNull IBinder transition,
@@ -901,10 +902,19 @@ public class PipTransition extends PipTransitionController implements
private boolean isLegacyEnter(@NonNull TransitionInfo info) {
TransitionInfo.Change pipChange = getPipChange(info);
- // If the only change in the changes list is a opening type PiP task,
- // then this is legacy-enter PiP.
- return pipChange != null && info.getChanges().size() == 1
- && (pipChange.getMode() == TRANSIT_TO_FRONT || pipChange.getMode() == TRANSIT_OPEN);
+ if (pipChange != null) {
+ if (mEnterAnimationType == ANIM_TYPE_ALPHA) {
+ // If enter animation type is force overridden to an alpha type,
+ // treat this as legacy, and reset the animation type to default (i.e. bounds type).
+ setEnterAnimationType(ANIM_TYPE_BOUNDS);
+ return true;
+ }
+ // If the only change in the changes list is a opening type PiP task,
+ // then this is legacy-enter PiP.
+ return info.getChanges().size() == 1
+ && TransitionUtil.isOpeningMode(pipChange.getMode());
+ }
+ return false;
}
private boolean isRemovePipTransition(@NonNull TransitionInfo info) {
@@ -951,6 +961,20 @@ public class PipTransition extends PipTransitionController implements
initActivityPos.y);
}
}
+
+ /**
+ * Sets the type of animation to run upon entering PiP.
+ *
+ * By default, {@link PipTransition} uses various signals from Transitions to figure out
+ * the animation type. But we should provide the ability to override this animation type to
+ * mixed handlers, for instance, as they can split up and modify incoming {@link TransitionInfo}
+ * to pass it onto multiple {@link Transitions.TransitionHandler}s.
+ */
+ @Override
+ public void setEnterAnimationType(@AnimationType int type) {
+ mEnterAnimationType = type;
+ }
+
void cacheAndStartTransitionAnimator(@NonNull ValueAnimator animator) {
mTransitionAnimator = animator;
mTransitionAnimator.start();
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 2d4d458292ea..4f2e028a1df0 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
@@ -311,6 +311,9 @@ public class RecentTasksController implements TaskStackListenerCallback,
public void onTaskAdded(RunningTaskInfo taskInfo) {
notifyRunningTaskAppeared(taskInfo);
+ if (!enableShellTopTaskTracking()) {
+ notifyRecentTasksChanged();
+ }
}
public void onTaskRemoved(RunningTaskInfo taskInfo) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index 0445add9cba9..13d87eda085b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -92,6 +92,10 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
getHolder().addCallback(this);
}
+ public TaskViewTaskController getController() {
+ return mTaskViewTaskController;
+ }
+
/**
* Launch a new activity.
*
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index d19a7eac6ad2..aef75e2dc99e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -448,7 +448,7 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
* have the pending info, we'll do it when we receive it in
* {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)}.
*/
- void setTaskNotFound() {
+ public void setTaskNotFound() {
mTaskNotFound = true;
if (mPendingInfo != null) {
cleanUpPendingTask();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 6c90a9060523..1eaae7ec83d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -66,7 +67,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
static final String TAG = "TaskViewTransitions";
/**
- * Map of {@link TaskViewTaskController} to {@link TaskViewRequestedState}.
+ * Map of {@link TaskViewTaskController} to {@link TaskViewRepository.TaskViewState}.
* <p>
* {@link TaskView} keeps a reference to the {@link TaskViewTaskController} instance and
* manages its lifecycle.
@@ -95,6 +96,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
final @WindowManager.TransitionType int mType;
final WindowContainerTransaction mWct;
final @NonNull TaskViewTaskController mTaskView;
+ ExternalTransition mExternalTransition;
IBinder mClaimed;
/**
@@ -182,6 +184,32 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
}
/**
+ * Starts or queues an "external" runnable into the pending queue. This means it will run
+ * in order relative to the local transitions.
+ *
+ * The external operation *must* call {@link #onExternalDone} once it has finished.
+ *
+ * In practice, the external is usually another transition on a different handler.
+ */
+ public void enqueueExternal(@NonNull TaskViewTaskController taskView, ExternalTransition ext) {
+ final PendingTransition pending = new PendingTransition(
+ TRANSIT_NONE, null /* wct */, taskView, null /* cookie */);
+ pending.mExternalTransition = ext;
+ mPending.add(pending);
+ startNextTransition();
+ }
+
+ /**
+ * An external transition run in this "queue" is required to call this once it becomes ready.
+ */
+ public void onExternalDone(IBinder key) {
+ final PendingTransition pending = findPending(key);
+ if (pending == null) return;
+ mPending.remove(pending);
+ startNextTransition();
+ }
+
+ /**
* Looks through the pending transitions for a opening transaction that matches the provided
* `taskView`.
*
@@ -191,6 +219,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
PendingTransition findPendingOpeningTransition(TaskViewTaskController taskView) {
for (int i = mPending.size() - 1; i >= 0; --i) {
if (mPending.get(i).mTaskView != taskView) continue;
+ if (mPending.get(i).mExternalTransition != null) continue;
if (TransitionUtil.isOpeningType(mPending.get(i).mType)) {
return mPending.get(i);
}
@@ -207,6 +236,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
PendingTransition findPending(TaskViewTaskController taskView, int type) {
for (int i = mPending.size() - 1; i >= 0; --i) {
if (mPending.get(i).mTaskView != taskView) continue;
+ if (mPending.get(i).mExternalTransition != null) continue;
if (mPending.get(i).mType == type) {
return mPending.get(i);
}
@@ -518,7 +548,11 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
// Wait for this to start animating.
return;
}
- pending.mClaimed = mTransitions.startTransition(pending.mType, pending.mWct, this);
+ if (pending.mExternalTransition != null) {
+ pending.mClaimed = pending.mExternalTransition.start();
+ } else {
+ pending.mClaimed = mTransitions.startTransition(pending.mType, pending.mWct, this);
+ }
}
@Override
@@ -641,7 +675,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
}
@VisibleForTesting
- void prepareOpenAnimation(TaskViewTaskController taskView,
+ public void prepareOpenAnimation(TaskViewTaskController taskView,
final boolean newTask,
SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction,
@@ -695,4 +729,10 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
taskView.notifyAppeared(newTask);
}
+
+ /** Interface for running an external transition in this object's pending queue. */
+ public interface ExternalTransition {
+ /** Starts a transition and returns an identifying key for lookup. */
+ IBinder start();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
index b0547a2a47b1..29a58d7f75dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -407,8 +407,6 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition {
mLeftoversHandler.mergeAnimation(
transition, info, t, mergeTarget, finishCallback);
}
- } else {
- mPipHandler.end();
}
return;
case TYPE_KEYGUARD:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
index 2133275cde61..30ffdac5cbba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
@@ -21,7 +21,7 @@ import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
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.PipTransitionController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.shared.TransitionUtil.isOpeningMode;
import static com.android.wm.shell.shared.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -148,6 +148,7 @@ public class MixedTransitionHelper {
}
}
+ pipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA);
if (PipUtils.isPip2ExperimentEnabled()) {
TransitionInfo pipInfo = subCopy(info, TRANSIT_PIP, false /* withChanges */);
pipInfo.getChanges().add(pipChange);
@@ -157,7 +158,6 @@ public class MixedTransitionHelper {
pipHandler.startAnimation(mixed.mTransition, pipInfo, startTransaction,
finishTransaction, finishCB);
} else {
- pipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA);
pipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
finishCB);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 51b0291cab91..fad1c9f848ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -751,7 +751,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
// been added, so they must be added here
decoration.addCaptionInset(wct);
mDesktopTasksController.moveTaskToDesktop(taskId, wct, source,
- /* remoteTransition= */ null);
+ /* remoteTransition= */ null, /* moveToDesktopCallback */ null);
decoration.closeHandleMenu();
if (source == DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index b179741b1259..fe9bdb027d18 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -542,6 +542,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
if (appHeader != null) {
appHeader.setAppName(name);
appHeader.setAppIcon(icon);
+ if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
+ notifyCaptionStateChanged();
+ }
}
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt
index d87da092cccf..1bc48f89ea6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoader.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.windowdecor.common
import android.annotation.DimenRes
-import android.app.ActivityManager
import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.content.pm.ActivityInfo
@@ -29,6 +28,7 @@ import com.android.launcher3.icons.BaseIconFactory
import com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT
import com.android.launcher3.icons.IconProvider
import com.android.wm.shell.R
+import com.android.wm.shell.common.UserProfileContexts
import com.android.wm.shell.shared.annotations.ShellBackgroundThread
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
@@ -41,10 +41,10 @@ import java.util.concurrent.ConcurrentHashMap
* A utility and cache for window decoration UI resources.
*/
class WindowDecorTaskResourceLoader(
- private val context: Context,
shellInit: ShellInit,
private val shellController: ShellController,
private val shellCommandHandler: ShellCommandHandler,
+ private val userProfilesContexts: UserProfileContexts,
private val iconProvider: IconProvider,
private val headerIconFactory: BaseIconFactory,
private val veilIconFactory: BaseIconFactory,
@@ -54,11 +54,12 @@ class WindowDecorTaskResourceLoader(
shellInit: ShellInit,
shellController: ShellController,
shellCommandHandler: ShellCommandHandler,
+ userProfileContexts: UserProfileContexts,
) : this(
- context,
shellInit,
shellController,
shellCommandHandler,
+ userProfileContexts,
IconProvider(context),
headerIconFactory = context.createIconFactory(R.dimen.desktop_mode_caption_icon_radius),
veilIconFactory = context.createIconFactory(R.dimen.desktop_mode_resize_veil_icon_size),
@@ -79,9 +80,6 @@ class WindowDecorTaskResourceLoader(
*/
private val existingTasks = mutableSetOf<Int>()
- @VisibleForTesting
- lateinit var currentUserContext: Context
-
init {
shellInit.addInitCallback(this::onInit, this)
}
@@ -90,14 +88,10 @@ class WindowDecorTaskResourceLoader(
shellCommandHandler.addDumpCallback(this::dump, this)
shellController.addUserChangeListener(object : UserChangeListener {
override fun onUserChanged(newUserId: Int, userContext: Context) {
- currentUserContext = userContext
// No need to hold on to resources for tasks of another profile.
taskToResourceCache.clear()
}
})
- currentUserContext = context.createContextAsUser(
- UserHandle.of(ActivityManager.getCurrentUser()), /* flags= */ 0
- )
}
/** Returns the user readable name for this task. */
@@ -158,15 +152,20 @@ class WindowDecorTaskResourceLoader(
private fun loadAppResources(taskInfo: RunningTaskInfo): AppResources {
Trace.beginSection("$TAG#loadAppResources")
- val pm = currentUserContext.packageManager
- val activityInfo = getActivityInfo(taskInfo, pm)
- val appName = pm.getApplicationLabel(activityInfo.applicationInfo)
- val appIconDrawable = iconProvider.getIcon(activityInfo)
- val badgedAppIconDrawable = pm.getUserBadgedIcon(appIconDrawable, taskInfo.userHandle())
- val appIcon = headerIconFactory.createIconBitmap(badgedAppIconDrawable, /* scale= */ 1f)
- val veilIcon = veilIconFactory.createScaledBitmap(appIconDrawable, MODE_DEFAULT)
- Trace.endSection()
- return AppResources(appName = appName, appIcon = appIcon, veilIcon = veilIcon)
+ try {
+ val pm = checkNotNull(userProfilesContexts[taskInfo.userId]?.packageManager) {
+ "Could not get context for user ${taskInfo.userId}"
+ }
+ val activityInfo = getActivityInfo(taskInfo, pm)
+ val appName = pm.getApplicationLabel(activityInfo.applicationInfo)
+ val appIconDrawable = iconProvider.getIcon(activityInfo)
+ val badgedAppIconDrawable = pm.getUserBadgedIcon(appIconDrawable, taskInfo.userHandle())
+ val appIcon = headerIconFactory.createIconBitmap(badgedAppIconDrawable, /* scale= */ 1f)
+ val veilIcon = veilIconFactory.createScaledBitmap(appIconDrawable, MODE_DEFAULT)
+ return AppResources(appName = appName, appIcon = appIcon, veilIcon = veilIcon)
+ } finally {
+ Trace.endSection()
+ }
}
private fun getActivityInfo(taskInfo: RunningTaskInfo, pm: PackageManager): ActivityInfo {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
index dc4fa3788778..79c5eff01b4c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
@@ -329,11 +329,6 @@ class AppHeaderViewHolder(
}
fun runOnAppChipGlobalLayout(runnable: () -> Unit) {
- if (openMenuButton.isAttachedToWindow) {
- // App chip is already inflated.
- runnable()
- return
- }
// Wait for app chip to be inflated before notifying repository.
openMenuButton.viewTreeObserver.addOnGlobalLayoutListener(object :
OnGlobalLayoutListener {
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsLandscape.kt
new file mode 100644
index 000000000000..6b159a4152ac
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppFromAllApps
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppFromAllAppsLandscape : OpenAppFromAllApps(rotation = ROTATION_90) {
+
+ @ExpectedScenarios(["CASCADE_APP"])
+ @Test
+ override fun openApp() = super.openApp()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsPortrait.kt
new file mode 100644
index 000000000000..07b439284680
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromAllAppsPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+import android.tools.Rotation.ROTATION_0
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppFromAllApps
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppFromAllAppsPortrait : OpenAppFromAllApps(rotation = ROTATION_0) {
+
+ @ExpectedScenarios(["CASCADE_APP"])
+ @Test
+ override fun openApp() = super.openApp()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarLandscape.kt
new file mode 100644
index 000000000000..caadd3be0b9c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppFromTaskbar
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppFromTaskbarLandscape : OpenAppFromTaskbar(rotation = ROTATION_90) {
+
+ @ExpectedScenarios(["CASCADE_APP"])
+ @Test
+ override fun openApp() = super.openApp()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarPortrait.kt
new file mode 100644
index 000000000000..77f5ab290e20
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppFromTaskbarPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+import android.tools.Rotation.ROTATION_0
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppFromTaskbar
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppFromTaskbarPortrait : OpenAppFromTaskbar(rotation = ROTATION_0) {
+
+ @ExpectedScenarios(["CASCADE_APP"])
+ @Test
+ override fun openApp() = super.openApp()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppFromAllApps.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppFromAllApps.kt
index 36cdd5b26992..348219631245 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppFromAllApps.kt
@@ -20,12 +20,12 @@ import android.app.Instrumentation
import android.tools.NavBar
import android.tools.flicker.rules.ChangeDisplayOrientationRule
import android.tools.Rotation
+import android.tools.device.apphelpers.CalculatorAppHelper
import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
-import com.android.server.wm.flicker.helpers.MailAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
import com.android.wm.shell.Utils
@@ -44,7 +44,7 @@ abstract class OpenAppFromAllApps(val rotation: Rotation = Rotation.ROTATION_0)
private val wmHelper = WindowManagerStateHelper(instrumentation)
private val device = UiDevice.getInstance(instrumentation)
private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
- private val mailApp = MailAppHelper(instrumentation)
+ private val calculatorApp = CalculatorAppHelper(instrumentation)
@Rule
@JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
@@ -64,13 +64,13 @@ abstract class OpenAppFromAllApps(val rotation: Rotation = Rotation.ROTATION_0)
open fun openApp() {
tapl.launchedAppState.taskbar
.openAllApps()
- .getAppIcon(mailApp.appName)
- .launch(mailApp.packageName)
+ .getAppIcon(calculatorApp.appName)
+ .launch(calculatorApp.packageName)
}
@After
fun teardown() {
- mailApp.exit(wmHelper)
+ calculatorApp.exit(wmHelper)
testApp.exit(wmHelper)
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
index f9c60ad14fae..aa893ed65e7c 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
@@ -19,7 +19,6 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
-import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.Rotation
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
@@ -29,7 +28,6 @@ import android.tools.helpers.WindowUtils
import android.tools.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.Assume
import org.junit.FixMethodOrder
@@ -64,7 +62,6 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
open class FromSplitScreenAutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
AutoEnterPipOnGoToHomeTest(flicker) {
private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
index 805f4c2fe7f8..8e7cb56c0f07 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
@@ -19,7 +19,6 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
-import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.Rotation
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
@@ -30,7 +29,6 @@ import android.tools.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.EnterPipTransition
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.Assume
@@ -66,7 +64,6 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
@FlakyTest(bugId = 386333280)
open class FromSplitScreenEnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) :
EnterPipTransition(flicker) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
new file mode 100644
index 000000000000..b4f514acf2dd
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.WindowManager.TRANSIT_CHANGE;
+
+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 static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.view.ViewRootImpl;
+import android.window.IWindowContainerToken;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.icons.BubbleIconFactory;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestSyncExecutor;
+import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
+import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.taskview.TaskView;
+import com.android.wm.shell.taskview.TaskViewRepository;
+import com.android.wm.shell.taskview.TaskViewTaskController;
+import com.android.wm.shell.taskview.TaskViewTransitions;
+import com.android.wm.shell.transition.Transitions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests of {@link BubbleTransitions}.
+ */
+@SmallTest
+public class BubbleTransitionsTest extends ShellTestCase {
+ @Mock
+ private BubbleData mBubbleData;
+ @Mock
+ private Bubble mBubble;
+ @Mock
+ private Transitions mTransitions;
+ @Mock
+ private SyncTransactionQueue mSyncQueue;
+ @Mock
+ private BubbleExpandedViewManager mExpandedViewManager;
+ @Mock
+ private BubblePositioner mBubblePositioner;
+ @Mock
+ private BubbleLogger mBubbleLogger;
+ @Mock
+ private BubbleStackView mStackView;
+ @Mock
+ private BubbleBarLayerView mLayerView;
+ @Mock
+ private BubbleIconFactory mIconFactory;
+
+ @Mock private ShellTaskOrganizer mTaskOrganizer;
+ private TaskViewTransitions mTaskViewTransitions;
+ private TaskViewRepository mRepository;
+ private BubbleTransitions mBubbleTransitions;
+ private BubbleTaskViewFactory mTaskViewFactory;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mRepository = new TaskViewRepository();
+ ShellExecutor syncExecutor = new TestSyncExecutor();
+
+ when(mTransitions.getMainExecutor()).thenReturn(syncExecutor);
+ when(mTransitions.isRegistered()).thenReturn(true);
+ mTaskViewTransitions = new TaskViewTransitions(mTransitions, mRepository, mTaskOrganizer,
+ mSyncQueue);
+ mBubbleTransitions = new BubbleTransitions(mTransitions, mTaskOrganizer, mRepository,
+ mBubbleData, mTaskViewTransitions, mContext);
+ mTaskViewFactory = () -> {
+ TaskViewTaskController taskViewTaskController = new TaskViewTaskController(
+ mContext, mTaskOrganizer, mTaskViewTransitions, mSyncQueue);
+ TaskView taskView = new TaskView(mContext, mTaskViewTransitions,
+ taskViewTaskController);
+ return new BubbleTaskView(taskView, syncExecutor);
+ };
+ final BubbleBarExpandedView bbev = mock(BubbleBarExpandedView.class);
+ final ViewRootImpl vri = mock(ViewRootImpl.class);
+ when(bbev.getViewRootImpl()).thenReturn(vri);
+ when(mBubble.getBubbleBarExpandedView()).thenReturn(bbev);
+ }
+
+ private ActivityManager.RunningTaskInfo setupBubble() {
+ ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
+ final IBinder asBinder = mock(IBinder.class);
+ when(itoken.asBinder()).thenReturn(asBinder);
+ WindowContainerToken token = new WindowContainerToken(itoken);
+ taskInfo.token = token;
+ final TaskView tv = mock(TaskView.class);
+ final TaskViewTaskController tvtc = mock(TaskViewTaskController.class);
+ when(tvtc.getTaskInfo()).thenReturn(taskInfo);
+ when(tv.getController()).thenReturn(tvtc);
+ when(mBubble.getTaskView()).thenReturn(tv);
+ mRepository.add(tvtc);
+ return taskInfo;
+ }
+
+ @Test
+ public void testConvertToBubble() {
+ // Basic walk-through of convert-to-bubble transition stages
+ ActivityManager.RunningTaskInfo taskInfo = setupBubble();
+ final BubbleTransitions.BubbleTransition bt = mBubbleTransitions.startConvertToBubble(
+ mBubble, taskInfo, mExpandedViewManager, mTaskViewFactory, mBubblePositioner,
+ mBubbleLogger, mStackView, mLayerView, mIconFactory, false);
+ final BubbleTransitions.ConvertToBubble ctb = (BubbleTransitions.ConvertToBubble) bt;
+ ctb.onInflated(mBubble);
+ when(mLayerView.canExpandView(any())).thenReturn(true);
+ verify(mTransitions).startTransition(anyInt(), any(), eq(ctb));
+ verify(mBubble).setPreparingTransition(eq(bt));
+ // Ensure we are communicating with the taskviewtransitions queue
+ assertTrue(mTaskViewTransitions.hasPending());
+
+ final TransitionInfo info = new TransitionInfo(TRANSIT_CHANGE, 0);
+ final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token,
+ mock(SurfaceControl.class));
+ chg.setTaskInfo(taskInfo);
+ chg.setMode(TRANSIT_CHANGE);
+ info.addChange(chg);
+ info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0));
+ SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ final boolean[] finishCalled = new boolean[]{false};
+ Transitions.TransitionFinishCallback finishCb = wct -> {
+ assertFalse(finishCalled[0]);
+ finishCalled[0] = true;
+ };
+ ctb.startAnimation(ctb.mTransition, info, startT, finishT, finishCb);
+ assertFalse(mTaskViewTransitions.hasPending());
+
+ verify(mBubbleData).notificationEntryUpdated(eq(mBubble), anyBoolean(), anyBoolean());
+ ctb.continueExpand();
+
+ clearInvocations(mBubble);
+ verify(mBubble, never()).setPreparingTransition(any());
+
+ ctb.surfaceCreated();
+ verify(mBubble).setPreparingTransition(isNull());
+ ArgumentCaptor<Runnable> animCb = ArgumentCaptor.forClass(Runnable.class);
+ verify(mLayerView).animateConvert(any(), any(), any(), any(), animCb.capture());
+ assertFalse(finishCalled[0]);
+ animCb.getValue().run();
+ assertTrue(finishCalled[0]);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/UserProfileContextsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/UserProfileContextsTest.kt
new file mode 100644
index 000000000000..ef0b8ab14c81
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/UserProfileContextsTest.kt
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.common
+
+import android.app.ActivityManager
+import android.content.Context
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.sysui.ShellController
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.sysui.UserChangeListener
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.anyInt
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for [UserProfileContexts].
+ */
+@RunWith(AndroidTestingRunner::class)
+class UserProfileContextsTest : ShellTestCase() {
+
+ private val testExecutor = TestShellExecutor()
+ private val shellInit = ShellInit(testExecutor)
+ private val activityManager = mock<ActivityManager>()
+ private val userManager = mock<UserManager>()
+ private val shellController = mock<ShellController>()
+ private val baseContext = mock<Context>()
+
+ private lateinit var userProfilesContexts: UserProfileContexts
+
+ @Before
+ fun setUp() {
+ doReturn(activityManager)
+ .whenever(baseContext)
+ .getSystemService(eq(ActivityManager::class.java))
+ doReturn(userManager).whenever(baseContext).getSystemService(eq(UserManager::class.java))
+ doAnswer { invocation ->
+ val userHandle = invocation.getArgument<UserHandle>(0)
+ createContextForUser(userHandle.identifier)
+ }
+ .whenever(baseContext)
+ .createContextAsUser(any<UserHandle>(), anyInt())
+ // Define users and profiles
+ val currentUser = ActivityManager.getCurrentUser()
+ whenever(userManager.getProfiles(eq(currentUser)))
+ .thenReturn(
+ listOf(UserInfo(currentUser, "Current", 0), UserInfo(MAIN_PROFILE, "Work", 0))
+ )
+ whenever(userManager.getProfiles(eq(SECOND_USER))).thenReturn(SECOND_PROFILES)
+ userProfilesContexts = UserProfileContexts(baseContext, shellController, shellInit)
+ shellInit.init()
+ }
+
+ @Test
+ fun onInit_registerUserChangeAndInit() {
+ val currentUser = ActivityManager.getCurrentUser()
+
+ verify(shellController, times(1)).addUserChangeListener(any())
+ assertThat(userProfilesContexts.userContext.userId).isEqualTo(currentUser)
+ assertThat(userProfilesContexts[currentUser]?.userId).isEqualTo(currentUser)
+ assertThat(userProfilesContexts[MAIN_PROFILE]?.userId).isEqualTo(MAIN_PROFILE)
+ assertThat(userProfilesContexts[SECOND_USER]).isNull()
+ }
+
+ @Test
+ fun onUserChanged_updateUserContext() {
+ val userChangeListener = retrieveUserChangeListener()
+ val newUserContext = createContextForUser(SECOND_USER)
+
+ userChangeListener.onUserChanged(SECOND_USER, newUserContext)
+
+ assertThat(userProfilesContexts.userContext).isEqualTo(newUserContext)
+ assertThat(userProfilesContexts[SECOND_USER]).isEqualTo(newUserContext)
+ }
+
+ @Test
+ fun onUserProfilesChanged_updateAllContexts() {
+ val userChangeListener = retrieveUserChangeListener()
+ val newUserContext = createContextForUser(SECOND_USER)
+ userChangeListener.onUserChanged(SECOND_USER, newUserContext)
+
+ userChangeListener.onUserProfilesChanged(SECOND_PROFILES)
+
+ assertThat(userProfilesContexts.userContext).isEqualTo(newUserContext)
+ assertThat(userProfilesContexts[SECOND_USER]).isEqualTo(newUserContext)
+ assertThat(userProfilesContexts[SECOND_PROFILE]?.userId).isEqualTo(SECOND_PROFILE)
+ assertThat(userProfilesContexts[SECOND_PROFILE_2]?.userId).isEqualTo(SECOND_PROFILE_2)
+ }
+
+ @Test
+ fun onUserProfilesChanged_keepOnlyNewProfiles() {
+ val userChangeListener = retrieveUserChangeListener()
+ val newUserContext = createContextForUser(SECOND_USER)
+ userChangeListener.onUserChanged(SECOND_USER, newUserContext)
+ userChangeListener.onUserProfilesChanged(SECOND_PROFILES)
+ val newProfiles = listOf(
+ UserInfo(SECOND_USER, "Second", 0),
+ UserInfo(SECOND_PROFILE, "Second Profile", 0),
+ UserInfo(MAIN_PROFILE, "Main profile", 0),
+ )
+
+ userChangeListener.onUserProfilesChanged(newProfiles)
+
+ assertThat(userProfilesContexts[SECOND_PROFILE_2]).isNull()
+ assertThat(userProfilesContexts[MAIN_PROFILE]?.userId).isEqualTo(MAIN_PROFILE)
+ assertThat(userProfilesContexts[SECOND_USER]?.userId).isEqualTo(SECOND_USER)
+ assertThat(userProfilesContexts[SECOND_PROFILE]?.userId).isEqualTo(SECOND_PROFILE)
+ }
+
+ private fun retrieveUserChangeListener(): UserChangeListener {
+ val captor = argumentCaptor<UserChangeListener>()
+
+ verify(shellController, times(1)).addUserChangeListener(captor.capture())
+
+ return captor.firstValue
+ }
+
+ private fun createContextForUser(userId: Int): Context {
+ val newContext = mock<Context>()
+ whenever(newContext.userId).thenReturn(userId)
+ return newContext
+ }
+
+ private companion object {
+ const val SECOND_USER = 3
+ const val MAIN_PROFILE = 11
+ const val SECOND_PROFILE = 15
+ const val SECOND_PROFILE_2 = 17
+
+ val SECOND_PROFILES =
+ listOf(
+ UserInfo(SECOND_USER, "Second", 0),
+ UserInfo(SECOND_PROFILE, "Second Profile", 0),
+ UserInfo(SECOND_PROFILE_2, "Second Profile 2", 0),
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
index ecad5217b87f..957fdf995776 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
@@ -183,6 +183,8 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
@Test
fun handleActivityOrientationChange_resizeable_doNothing() {
+ userRepositories.current.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ userRepositories.current.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val task = setUpFreeformTask()
taskStackListener.onActivityRequestedOrientationChanged(
@@ -195,6 +197,8 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
@Test
fun handleActivityOrientationChange_nonResizeableFullscreen_doNothing() {
+ userRepositories.current.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ userRepositories.current.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val task = createFullscreenTask()
task.isResizeable = false
val activityInfo = ActivityInfo()
@@ -214,6 +218,8 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
@Test
fun handleActivityOrientationChange_nonResizeablePortrait_requestSameOrientation_doNothing() {
+ userRepositories.current.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ userRepositories.current.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val task = setUpFreeformTask(isResizeable = false)
val newTask =
setUpFreeformTask(
@@ -228,6 +234,8 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
@Test
fun handleActivityOrientationChange_notInDesktopMode_doNothing() {
+ userRepositories.current.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ userRepositories.current.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val task = setUpFreeformTask(isResizeable = false)
userRepositories.current.updateTask(task.displayId, task.taskId, isVisible = false)
@@ -241,6 +249,8 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
@Test
fun handleActivityOrientationChange_nonResizeablePortrait_respectLandscapeRequest() {
+ userRepositories.current.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ userRepositories.current.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val task = setUpFreeformTask(isResizeable = false)
val oldBounds = task.configuration.windowConfiguration.bounds
val newTask =
@@ -263,6 +273,8 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
@Test
fun handleActivityOrientationChange_nonResizeableLandscape_respectPortraitRequest() {
+ userRepositories.current.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ userRepositories.current.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val oldBounds = Rect(0, 0, 500, 200)
val task =
setUpFreeformTask(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
index 6a3717427e93..5d8d5a716504 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
@@ -20,6 +20,8 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.content.ContentResolver
import android.os.Binder
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings
import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
import android.testing.AndroidTestingRunner
@@ -29,20 +31,25 @@ import android.view.WindowManager.TRANSIT_CHANGE
import android.window.DisplayAreaInfo
import android.window.WindowContainerTransaction
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.window.flags.Flags
import com.android.wm.shell.MockToken
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
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.ArgumentCaptor
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mock
import org.mockito.Mockito.anyInt
@@ -53,6 +60,7 @@ import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
/**
* Test class for [DesktopDisplayEventHandler]
@@ -63,18 +71,33 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
class DesktopDisplayEventHandlerTest : ShellTestCase() {
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
@Mock lateinit var testExecutor: ShellExecutor
@Mock lateinit var transitions: Transitions
@Mock lateinit var displayController: DisplayController
@Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
@Mock private lateinit var mockWindowManager: IWindowManager
+ @Mock private lateinit var mockDesktopUserRepositories: DesktopUserRepositories
+ @Mock private lateinit var mockDesktopRepository: DesktopRepository
+ @Mock private lateinit var mockDesktopTasksController: DesktopTasksController
+ private lateinit var mockitoSession: StaticMockitoSession
private lateinit var shellInit: ShellInit
private lateinit var handler: DesktopDisplayEventHandler
+ private val onDisplaysChangedListenerCaptor = argumentCaptor<OnDisplaysChangedListener>()
+
@Before
fun setUp() {
+ mockitoSession =
+ mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(DesktopModeStatus::class.java)
+ .startMocking()
+
shellInit = spy(ShellInit(testExecutor))
+ whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository)
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
@@ -86,8 +109,17 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
displayController,
rootTaskDisplayAreaOrganizer,
mockWindowManager,
+ mockDesktopUserRepositories,
+ mockDesktopTasksController,
)
shellInit.init()
+ verify(displayController)
+ .addDisplayWindowListener(onDisplaysChangedListenerCaptor.capture())
+ }
+
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
}
private fun testDisplayWindowingModeSwitch(
@@ -96,8 +128,6 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
expectTransition: Boolean,
) {
val externalDisplayId = 100
- val captor = ArgumentCaptor.forClass(OnDisplaysChangedListener::class.java)
- verify(displayController).addDisplayWindowListener(captor.capture())
val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
tda.configuration.windowConfiguration.windowingMode = defaultWindowingMode
whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { defaultWindowingMode }
@@ -111,12 +141,12 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
// The external display connected.
whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
.thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId))
- captor.value.onDisplayAdded(externalDisplayId)
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId)
tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
// The external display disconnected.
whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
.thenReturn(intArrayOf(DEFAULT_DISPLAY))
- captor.value.onDisplayRemoved(externalDisplayId)
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId)
if (expectTransition) {
val arg = argumentCaptor<WindowContainerTransaction>()
@@ -159,6 +189,44 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
)
}
+ @Test
+ fun testDisplayAdded_supportsDesks_createsDesk() {
+ whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
+
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+
+ verify(mockDesktopTasksController).createDesk(DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun testDisplayAdded_cannotEnterDesktopMode_doesNotCreateDesk() {
+ whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
+
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+
+ verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun testDeskRemoved_noDesksRemain_createsDesk() {
+ whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(0)
+
+ handler.onDeskRemoved(DEFAULT_DISPLAY, deskId = 1)
+
+ verify(mockDesktopTasksController).createDesk(DEFAULT_DISPLAY)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun testDeskRemoved_desksRemain_doesNotCreateDesk() {
+ whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(1)
+
+ handler.onDeskRemoved(DEFAULT_DISPLAY, deskId = 1)
+
+ verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
+ }
+
private class ExtendedDisplaySettingsSession(
private val contentResolver: ContentResolver,
private val overrideValue: Int,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index 8d73f3f59afd..5736793cd6a9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -18,12 +18,13 @@ package com.android.wm.shell.desktopmode
import android.graphics.Rect
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import android.platform.test.flag.junit.SetFlagsRule
-import android.testing.AndroidTestingRunner
import android.util.ArraySet
import android.view.Display.DEFAULT_DISPLAY
import android.view.Display.INVALID_DISPLAY
import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP
import com.android.wm.shell.ShellTestCase
@@ -56,6 +57,8 @@ import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
/**
* Tests for [@link DesktopRepository].
@@ -63,11 +66,11 @@ import org.mockito.kotlin.whenever
* Build/Install/Run: atest WMShellUnitTests:DesktopRepositoryTest
*/
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@ExperimentalCoroutinesApi
-class DesktopRepositoryTest : ShellTestCase() {
+class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
+ @JvmField @Rule val setFlagsRule = SetFlagsRule(flags)
private lateinit var repo: DesktopRepository
private lateinit var shellInit: ShellInit
@@ -86,6 +89,8 @@ class DesktopRepositoryTest : ShellTestCase() {
whenever(runBlocking { persistentRepository.readDesktop(any(), any()) })
.thenReturn(Desktop.getDefaultInstance())
shellInit.init()
+ repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = DEFAULT_DESKTOP_ID)
+ repo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = DEFAULT_DESKTOP_ID)
}
@After
@@ -137,6 +142,7 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
fun addTask_multipleDisplays_notifiesCorrectListener() {
+ repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
val listener = TestListener()
repo.addActiveTaskListener(listener)
@@ -150,6 +156,7 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
fun addTask_multipleDisplays_moveToAnotherDisplay() {
+ repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
repo.addTask(SECOND_DISPLAY, taskId = 1, isVisible = true)
assertThat(repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)).isEmpty()
@@ -310,19 +317,21 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
fun isOnlyVisibleNonClosingTask_multipleDisplays() {
+ repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
+ repo.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
repo.updateTask(DEFAULT_DISPLAY, taskId = 2, isVisible = true)
repo.updateTask(SECOND_DISPLAY, taskId = 3, isVisible = true)
// Not the only task on DEFAULT_DISPLAY
assertThat(repo.isVisibleTask(1)).isTrue()
- assertThat(repo.isOnlyVisibleNonClosingTask(1)).isFalse()
+ assertThat(repo.isOnlyVisibleNonClosingTask(1, DEFAULT_DISPLAY)).isFalse()
// Not the only task on DEFAULT_DISPLAY
assertThat(repo.isVisibleTask(2)).isTrue()
- assertThat(repo.isOnlyVisibleNonClosingTask(2)).isFalse()
+ assertThat(repo.isOnlyVisibleNonClosingTask(2, DEFAULT_DISPLAY)).isFalse()
// The only visible task on SECOND_DISPLAY
assertThat(repo.isVisibleTask(3)).isTrue()
- assertThat(repo.isOnlyVisibleNonClosingTask(3)).isTrue()
+ assertThat(repo.isOnlyVisibleNonClosingTask(3, SECOND_DISPLAY)).isTrue()
// Not a visible task
assertThat(repo.isVisibleTask(99)).isFalse()
assertThat(repo.isOnlyVisibleNonClosingTask(99)).isFalse()
@@ -343,6 +352,7 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
fun addListener_tasksOnDifferentDisplay_doesNotNotify() {
+ repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
repo.updateTask(SECOND_DISPLAY, taskId = 1, isVisible = true)
val listener = TestVisibilityListener()
val executor = TestShellExecutor()
@@ -351,7 +361,7 @@ class DesktopRepositoryTest : ShellTestCase() {
assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(0)
// One call as adding listener notifies it
- assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(0)
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1)
}
@Test
@@ -365,11 +375,14 @@ class DesktopRepositoryTest : ShellTestCase() {
executor.flushAll()
assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(2)
- assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(2)
+ // 1 from registration, 2 for the updates.
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(3)
}
@Test
fun updateTask_visibleTask_addVisibleTaskNotifiesListenerForThatDisplay() {
+ repo.addDesk(displayId = 1, deskId = 1)
+ repo.setActiveDesk(displayId = 1, deskId = 1)
val listener = TestVisibilityListener()
val executor = TestShellExecutor()
repo.addVisibleTasksListener(listener, executor)
@@ -378,22 +391,27 @@ class DesktopRepositoryTest : ShellTestCase() {
executor.flushAll()
assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(1)
- assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1)
+ // 1 for the registration, 1 for the update.
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(2)
assertThat(listener.visibleTasksCountOnSecondaryDisplay).isEqualTo(0)
- assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(0)
+ // 1 for the registration.
+ assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(1)
repo.updateTask(displayId = 1, taskId = 2, isVisible = true)
executor.flushAll()
// Listener for secondary display is notified
assertThat(listener.visibleTasksCountOnSecondaryDisplay).isEqualTo(1)
- assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(1)
+ // 1 for the registration, 1 for the update.
+ assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(2)
// No changes to listener for default display
- assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1)
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(2)
}
@Test
fun updateTask_taskOnDefaultBecomesVisibleOnSecondDisplay_listenersNotified() {
+ repo.addDesk(displayId = 1, deskId = 1)
+ repo.setActiveDesk(displayId = 1, deskId = 1)
val listener = TestVisibilityListener()
val executor = TestShellExecutor()
repo.addVisibleTasksListener(listener, executor)
@@ -406,14 +424,15 @@ class DesktopRepositoryTest : ShellTestCase() {
repo.updateTask(displayId = 1, taskId = 1, isVisible = true)
executor.flushAll()
- // Default display should have 2 calls
- // 1 - visible task added
- // 2 - visible task removed
- assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(2)
+ // Default display should have 3 calls
+ // 1 - listener registered
+ // 2 - visible task added
+ // 3 - visible task removed
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(3)
assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(0)
- // Secondary display should have 1 call for visible task added
- assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(1)
+ // Secondary display should have 2 calls for registration + visible task added
+ assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(2)
assertThat(listener.visibleTasksCountOnSecondaryDisplay).isEqualTo(1)
}
@@ -431,13 +450,13 @@ class DesktopRepositoryTest : ShellTestCase() {
repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = false)
executor.flushAll()
- assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(3)
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(4)
repo.updateTask(DEFAULT_DISPLAY, taskId = 2, isVisible = false)
executor.flushAll()
assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(0)
- assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(4)
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(5)
}
/**
@@ -458,7 +477,8 @@ class DesktopRepositoryTest : ShellTestCase() {
repo.updateTask(INVALID_DISPLAY, taskId = 1, isVisible = false)
executor.flushAll()
- assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(3)
+ // 1 from registering, 1x3 for each update including the one to the invalid display.
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(4)
assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(1)
}
@@ -497,6 +517,8 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
fun getVisibleTaskCount_multipleDisplays_returnsCorrectCount() {
+ repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
+ repo.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(0)
@@ -674,8 +696,6 @@ class DesktopRepositoryTest : ShellTestCase() {
repo.removeTask(INVALID_DISPLAY, taskId = 1)
- val invalidDisplayTasks = repo.getFreeformTasksInZOrder(INVALID_DISPLAY)
- assertThat(invalidDisplayTasks).isEmpty()
val validDisplayTasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
assertThat(validDisplayTasks).isEmpty()
}
@@ -746,6 +766,7 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
fun removeTask_validDisplay_differentDisplay_doesNotRemovesTask() {
+ repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
repo.removeTask(SECOND_DISPLAY, taskId = 1)
@@ -758,6 +779,7 @@ class DesktopRepositoryTest : ShellTestCase() {
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
fun removeTask_validDisplayButDifferentDisplay_persistenceEnabled_doesNotRemoveTask() {
runTest(StandardTestDispatcher()) {
+ repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
repo.removeTask(SECOND_DISPLAY, taskId = 1)
@@ -784,10 +806,10 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
fun removeTask_removesTaskBoundsBeforeMaximize() {
val taskId = 1
- repo.addTask(THIRD_DISPLAY, taskId, isVisible = true)
+ repo.addTask(DEFAULT_DISPLAY, taskId, isVisible = true)
repo.saveBoundsBeforeMaximize(taskId, Rect(0, 0, 200, 200))
- repo.removeTask(THIRD_DISPLAY, taskId)
+ repo.removeTask(DEFAULT_DISPLAY, taskId)
assertThat(repo.removeBoundsBeforeMaximize(taskId)).isNull()
}
@@ -795,16 +817,17 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
fun removeTask_removesTaskBoundsBeforeImmersive() {
val taskId = 1
- repo.addTask(THIRD_DISPLAY, taskId, isVisible = true)
+ repo.addTask(DEFAULT_DISPLAY, taskId, isVisible = true)
repo.saveBoundsBeforeFullImmersive(taskId, Rect(0, 0, 200, 200))
- repo.removeTask(THIRD_DISPLAY, taskId)
+ repo.removeTask(DEFAULT_DISPLAY, taskId)
assertThat(repo.removeBoundsBeforeFullImmersive(taskId)).isNull()
}
@Test
fun removeTask_removesActiveTask() {
+ repo.addDesk(THIRD_DISPLAY, THIRD_DISPLAY)
val taskId = 1
val listener = TestListener()
repo.addActiveTaskListener(listener)
@@ -829,6 +852,7 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
fun removeTask_updatesTaskVisibility() {
+ repo.addDesk(displayId = THIRD_DISPLAY, deskId = THIRD_DISPLAY)
val taskId = 1
repo.addTask(DEFAULT_DISPLAY, taskId, isVisible = true)
@@ -930,8 +954,8 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
fun updateTask_minimizedTaskBecomesVisible_unminimizesTask() {
- repo.minimizeTask(displayId = 10, taskId = 2)
- repo.updateTask(displayId = 10, taskId = 2, isVisible = true)
+ repo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2)
+ repo.updateTask(displayId = DEFAULT_DISPLAY, taskId = 2, isVisible = true)
val isMinimizedTask = repo.isMinimizedTask(taskId = 2)
@@ -1003,34 +1027,34 @@ class DesktopRepositoryTest : ShellTestCase() {
fun setTaskInFullImmersiveState_savedAsInImmersiveState() {
assertThat(repo.isTaskInFullImmersiveState(taskId = 1)).isFalse()
- repo.setTaskInFullImmersiveState(DEFAULT_DESKTOP_ID, taskId = 1, immersive = true)
+ repo.setTaskInFullImmersiveState(DEFAULT_DISPLAY, taskId = 1, immersive = true)
assertThat(repo.isTaskInFullImmersiveState(taskId = 1)).isTrue()
}
@Test
fun removeTaskInFullImmersiveState_removedAsInImmersiveState() {
- repo.setTaskInFullImmersiveState(DEFAULT_DESKTOP_ID, taskId = 1, immersive = true)
+ repo.setTaskInFullImmersiveState(DEFAULT_DISPLAY, taskId = 1, immersive = true)
assertThat(repo.isTaskInFullImmersiveState(taskId = 1)).isTrue()
- repo.setTaskInFullImmersiveState(DEFAULT_DESKTOP_ID, taskId = 1, immersive = false)
+ repo.setTaskInFullImmersiveState(DEFAULT_DISPLAY, taskId = 1, immersive = false)
assertThat(repo.isTaskInFullImmersiveState(taskId = 1)).isFalse()
}
@Test
fun removeTaskInFullImmersiveState_otherWasImmersive_otherRemainsImmersive() {
- repo.setTaskInFullImmersiveState(DEFAULT_DESKTOP_ID, taskId = 1, immersive = true)
+ repo.setTaskInFullImmersiveState(DEFAULT_DISPLAY, taskId = 1, immersive = true)
- repo.setTaskInFullImmersiveState(DEFAULT_DESKTOP_ID, taskId = 2, immersive = false)
+ repo.setTaskInFullImmersiveState(DEFAULT_DISPLAY, taskId = 2, immersive = false)
assertThat(repo.isTaskInFullImmersiveState(taskId = 1)).isTrue()
}
@Test
fun setTaskInFullImmersiveState_sameDisplay_overridesExistingFullImmersiveTask() {
- repo.setTaskInFullImmersiveState(DEFAULT_DESKTOP_ID, taskId = 1, immersive = true)
- repo.setTaskInFullImmersiveState(DEFAULT_DESKTOP_ID, taskId = 2, immersive = true)
+ repo.setTaskInFullImmersiveState(DEFAULT_DISPLAY, taskId = 1, immersive = true)
+ repo.setTaskInFullImmersiveState(DEFAULT_DISPLAY, taskId = 2, immersive = true)
assertThat(repo.isTaskInFullImmersiveState(taskId = 1)).isFalse()
assertThat(repo.isTaskInFullImmersiveState(taskId = 2)).isTrue()
@@ -1038,8 +1062,10 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
fun setTaskInFullImmersiveState_differentDisplay_bothAreImmersive() {
- repo.setTaskInFullImmersiveState(DEFAULT_DESKTOP_ID, taskId = 1, immersive = true)
- repo.setTaskInFullImmersiveState(DEFAULT_DESKTOP_ID + 1, taskId = 2, immersive = true)
+ repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
+ repo.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
+ repo.setTaskInFullImmersiveState(DEFAULT_DISPLAY, taskId = 1, immersive = true)
+ repo.setTaskInFullImmersiveState(SECOND_DISPLAY, taskId = 2, immersive = true)
assertThat(repo.isTaskInFullImmersiveState(taskId = 1)).isTrue()
assertThat(repo.isTaskInFullImmersiveState(taskId = 2)).isTrue()
@@ -1061,11 +1087,13 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
fun getTaskInFullImmersiveState_byDisplay() {
+ repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
+ repo.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
repo.setTaskInFullImmersiveState(DEFAULT_DESKTOP_ID, taskId = 1, immersive = true)
- repo.setTaskInFullImmersiveState(DEFAULT_DESKTOP_ID + 1, taskId = 2, immersive = true)
+ repo.setTaskInFullImmersiveState(SECOND_DISPLAY, taskId = 2, immersive = true)
assertThat(repo.getTaskInFullImmersiveState(DEFAULT_DESKTOP_ID)).isEqualTo(1)
- assertThat(repo.getTaskInFullImmersiveState(DEFAULT_DESKTOP_ID + 1)).isEqualTo(2)
+ assertThat(repo.getTaskInFullImmersiveState(SECOND_DISPLAY)).isEqualTo(2)
}
@Test
@@ -1089,11 +1117,13 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
fun setTaskInPip_multipleDisplays_bothAreInPip() {
+ repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
+ repo.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
repo.setTaskInPip(DEFAULT_DESKTOP_ID, taskId = 1, enterPip = true)
- repo.setTaskInPip(DEFAULT_DESKTOP_ID + 1, taskId = 2, enterPip = true)
+ repo.setTaskInPip(SECOND_DISPLAY, taskId = 2, enterPip = true)
assertThat(repo.isTaskMinimizedPipInDisplay(DEFAULT_DESKTOP_ID, taskId = 1)).isTrue()
- assertThat(repo.isTaskMinimizedPipInDisplay(DEFAULT_DESKTOP_ID + 1, taskId = 2)).isTrue()
+ assertThat(repo.isTaskMinimizedPipInDisplay(SECOND_DISPLAY, taskId = 2)).isTrue()
}
@Test
@@ -1169,5 +1199,10 @@ class DesktopRepositoryTest : ShellTestCase() {
const val THIRD_DISPLAY = 345
private const val DEFAULT_USER_ID = 1000
private const val DEFAULT_DESKTOP_ID = 0
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> =
+ FlagsParameterization.allCombinationsOf(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
index c7c0dfc5be6d..12c7ff61399f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
@@ -144,6 +144,16 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {
}
@Test
+ fun onTaskMovingToFront_freeformTaskOutsideDesktop_addsTaskToRepo() {
+ val task = createFullscreenTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+
+ desktopTaskChangeListener.onTaskMovingToFront(task)
+
+ verify(desktopUserRepositories.current).addTask(task.displayId, task.taskId, task.isVisible)
+ }
+
+ @Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
fun onTaskClosing_backNavEnabled_nonClosingTask_minimizesTaskInRepo() {
val task = createFreeformTask().apply { isVisible = true }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index db19017ef87a..46222674ff05 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -48,8 +48,8 @@ import android.os.IBinder
import android.os.UserManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import android.platform.test.flag.junit.SetFlagsRule
-import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
import android.view.DragEvent
import android.view.Gravity
@@ -100,6 +100,7 @@ import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.common.UserProfileContexts
import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitResult
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
@@ -117,6 +118,7 @@ import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCR
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
import com.android.wm.shell.desktopmode.persistence.Desktop
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
@@ -184,6 +186,8 @@ import org.mockito.kotlin.capture
import org.mockito.kotlin.eq
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
/**
* Test class for {@link DesktopTasksController}
@@ -191,12 +195,12 @@ import org.mockito.quality.Strictness
* Usage: atest WMShellUnitTests:DesktopTasksControllerTest
*/
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@ExperimentalCoroutinesApi
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-class DesktopTasksControllerTest : ShellTestCase() {
+class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() {
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
+ @JvmField @Rule val setFlagsRule = SetFlagsRule(flags)
@Mock lateinit var testExecutor: ShellExecutor
@Mock lateinit var shellCommandHandler: ShellCommandHandler
@@ -245,6 +249,10 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Mock
private lateinit var desktopWallpaperActivityTokenProvider:
DesktopWallpaperActivityTokenProvider
+ @Mock
+ private lateinit var overviewToDesktopTransitionObserver: OverviewToDesktopTransitionObserver
+ @Mock private lateinit var desksOrganizer: DesksOrganizer
+ @Mock private lateinit var userProfileContexts: UserProfileContexts
private lateinit var controller: DesktopTasksController
private lateinit var shellInit: ShellInit
@@ -339,6 +347,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
)
.thenReturn(ExitResult.NoExit)
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
+ whenever(userProfileContexts[anyInt()]).thenReturn(context)
controller = createController()
controller.setSplitScreenController(splitScreenController)
@@ -356,6 +365,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
taskRepository = userRepositories.current
+ taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = DEFAULT_DISPLAY)
+ taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = DEFAULT_DISPLAY)
}
private fun createController() =
@@ -392,6 +403,9 @@ class DesktopTasksControllerTest : ShellTestCase() {
desktopTilingDecorViewModel,
desktopWallpaperActivityTokenProvider,
Optional.of(bubbleController),
+ overviewToDesktopTransitionObserver,
+ desksOrganizer,
+ userProfileContexts,
)
@After
@@ -610,7 +624,12 @@ class DesktopTasksControllerTest : ShellTestCase() {
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
)
+ @DisableFlags(
+ /** TODO: b/362720497 - re-enable when activation is implemented. */
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND
+ )
fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_perDisplayWallpaperEnabled_shouldShowWallpaper() {
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
val homeTask = setUpHomeTask(SECOND_DISPLAY)
val task1 = setUpFreeformTask(SECOND_DISPLAY)
val task2 = setUpFreeformTask(SECOND_DISPLAY)
@@ -631,8 +650,13 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- @DisableFlags(Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY)
+ @DisableFlags(
+ Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
+ /** TODO: b/362720497 - re-enable when activation is implemented. */
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_shouldNotShowWallpaper() {
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
val homeTask = setUpHomeTask(SECOND_DISPLAY)
val task1 = setUpFreeformTask(SECOND_DISPLAY)
val task2 = setUpFreeformTask(SECOND_DISPLAY)
@@ -671,8 +695,13 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ @DisableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ /** TODO: b/362720497 - re-enable when activation is implemented. */
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
fun showDesktopApps_onSecondaryDisplay_desktopWallpaperDisabled_shouldNotMoveLauncher() {
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
val homeTask = setUpHomeTask(SECOND_DISPLAY)
val task1 = setUpFreeformTask(SECOND_DISPLAY)
val task2 = setUpFreeformTask(SECOND_DISPLAY)
@@ -774,6 +803,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperDisabled() {
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
setUpHomeTask(SECOND_DISPLAY)
@@ -794,6 +824,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperEnabled() {
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
setUpHomeTask(SECOND_DISPLAY)
@@ -880,6 +911,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
fun visibleTaskCount_twoTasksVisibleOnDifferentDisplays_returnsOne() {
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
+ taskRepository.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
setUpHomeTask()
setUpFreeformTask(DEFAULT_DISPLAY).also(::markTaskVisible)
setUpFreeformTask(SECOND_DISPLAY).also(::markTaskVisible)
@@ -1473,6 +1506,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val fullscreenTaskDefault = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
markTaskHidden(freeformTaskDefault)
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
val homeTaskSecond = setUpHomeTask(displayId = SECOND_DISPLAY)
val freeformTaskSecond = setUpFreeformTask(displayId = SECOND_DISPLAY)
markTaskHidden(freeformTaskSecond)
@@ -1670,6 +1704,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
fun moveToFullscreen_secondDisplayTaskHasFreeform_secondDisplayNotAffected() {
val taskDefaultDisplay = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
val taskSecondDisplay = setUpFreeformTask(displayId = SECOND_DISPLAY)
controller.moveToFullscreen(taskDefaultDisplay.taskId, transitionSource = UNKNOWN)
@@ -1850,6 +1885,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
fun moveToNextDisplay_moveFromFirstToSecondDisplay() {
// Set up two display ids
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
whenever(rootTaskDisplayAreaOrganizer.displayIds)
.thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
// Create a mock for the target display area: second display
@@ -1879,6 +1915,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
.thenReturn(defaultDisplayArea)
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
val task = setUpFreeformTask(displayId = SECOND_DISPLAY)
controller.moveToNextDisplay(task.taskId)
@@ -1898,6 +1935,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
)
fun moveToNextDisplay_wallpaperOnSystemUser_reorderWallpaperToBack() {
// Set up two display ids
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
whenever(rootTaskDisplayAreaOrganizer.displayIds)
.thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
// Create a mock for the target display area: second display
@@ -1922,6 +1960,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
fun moveToNextDisplay_wallpaperNotOnSystemUser_removeWallpaper() {
// Set up two display ids
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
whenever(rootTaskDisplayAreaOrganizer.displayIds)
.thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
// Create a mock for the target display area: second display
@@ -2046,6 +2085,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT)
fun moveToNextDisplay_defaultBoundsWhenDestinationTooSmall() {
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
// Set up two display ids
whenever(rootTaskDisplayAreaOrganizer.displayIds)
.thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
@@ -2087,6 +2127,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT,
)
fun moveToNextDisplay_destinationGainGlobalFocus() {
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
// Set up two display ids
whenever(rootTaskDisplayAreaOrganizer.displayIds)
.thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
@@ -3155,6 +3196,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_closeTransition_singleTaskNoToken_secondaryDisplay_launchesHome() {
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
+ taskRepository.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
val task = setUpFreeformTask(displayId = SECOND_DISPLAY)
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
@@ -4930,7 +4973,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun shouldPlayDesktopAnimation_notShowingDesktop_doesNotPlay() {
- val triggerTask = setUpFullscreenTask(displayId = 5)
+ val triggerTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
taskRepository.setTaskInFullImmersiveState(
displayId = triggerTask.displayId,
taskId = triggerTask.taskId,
@@ -4948,7 +4991,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun shouldPlayDesktopAnimation_notOpening_doesNotPlay() {
- val triggerTask = setUpFreeformTask(displayId = 5)
+ val triggerTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
taskRepository.setTaskInFullImmersiveState(
displayId = triggerTask.displayId,
taskId = triggerTask.taskId,
@@ -4966,7 +5009,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun shouldPlayDesktopAnimation_notImmersive_doesNotPlay() {
- val triggerTask = setUpFreeformTask(displayId = 5)
+ val triggerTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
taskRepository.setTaskInFullImmersiveState(
displayId = triggerTask.displayId,
taskId = triggerTask.taskId,
@@ -4985,8 +5028,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun shouldPlayDesktopAnimation_fullscreenEntersDesktop_plays() {
// At least one freeform task to be in a desktop.
- val existingTask = setUpFreeformTask(displayId = 5)
- val triggerTask = setUpFullscreenTask(displayId = 5)
+ val existingTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val triggerTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isTrue()
taskRepository.setTaskInFullImmersiveState(
displayId = existingTask.displayId,
@@ -5005,7 +5048,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun shouldPlayDesktopAnimation_fullscreenStaysFullscreen_doesNotPlay() {
- val triggerTask = setUpFullscreenTask(displayId = 5)
+ val triggerTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse()
assertThat(
@@ -5020,8 +5063,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun shouldPlayDesktopAnimation_freeformStaysInDesktop_plays() {
// At least one freeform task to be in a desktop.
- val existingTask = setUpFreeformTask(displayId = 5)
- val triggerTask = setUpFreeformTask(displayId = 5, active = false)
+ val existingTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val triggerTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, active = false)
assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isTrue()
taskRepository.setTaskInFullImmersiveState(
displayId = existingTask.displayId,
@@ -5040,7 +5083,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun shouldPlayDesktopAnimation_freeformExitsDesktop_doesNotPlay() {
- val triggerTask = setUpFreeformTask(displayId = 5, active = false)
+ val triggerTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, active = false)
assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse()
assertThat(
@@ -5051,6 +5094,19 @@ class DesktopTasksControllerTest : ShellTestCase() {
.isFalse()
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun testCreateDesk() {
+ val currentDeskCount = taskRepository.getNumberOfDesks(DEFAULT_DISPLAY)
+ whenever(desksOrganizer.createDesk(eq(DEFAULT_DISPLAY), any())).thenAnswer { invocation ->
+ (invocation.arguments[1] as DesksOrganizer.OnCreateCallback).onCreated(deskId = 5)
+ }
+
+ controller.createDesk(DEFAULT_DISPLAY)
+
+ assertThat(taskRepository.getNumberOfDesks(DEFAULT_DISPLAY)).isEqualTo(currentDeskCount + 1)
+ }
+
private class RunOnStartTransitionCallback : ((IBinder) -> Unit) {
var invocations = 0
private set
@@ -5384,6 +5440,11 @@ class DesktopTasksControllerTest : ShellTestCase() {
val STABLE_BOUNDS = Rect(0, 0, 1000, 1000)
const val MAX_TASK_LIMIT = 6
private const val TASKBAR_FRAME_HEIGHT = 200
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> =
+ FlagsParameterization.allCombinationsOf(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index e85901bbd9d4..554b09f130bd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -180,6 +180,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun addPendingMinimizeTransition_taskIsNotMinimized() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val task = setUpFreeformTask()
markTaskHidden(task)
@@ -190,6 +192,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun onTransitionReady_noPendingTransition_taskIsNotMinimized() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val task = setUpFreeformTask()
markTaskHidden(task)
@@ -203,6 +207,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun onTransitionReady_differentPendingTransition_taskIsNotMinimized() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val pendingTransition = Binder()
val taskTransition = Binder()
val task = setUpFreeformTask()
@@ -219,6 +225,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun onTransitionReady_pendingTransition_noTaskChange_taskVisible_taskIsNotMinimized() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val transition = Binder()
val task = setUpFreeformTask()
markTaskVisible(task)
@@ -232,6 +240,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun onTransitionReady_pendingTransition_noTaskChange_taskInvisible_taskIsMinimized() {
val transition = Binder()
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val task = setUpFreeformTask()
markTaskHidden(task)
addPendingMinimizeChange(transition, taskId = task.taskId)
@@ -243,6 +253,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun onTransitionReady_pendingTransition_changeTaskToBack_taskIsMinimized() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val transition = Binder()
val task = setUpFreeformTask()
addPendingMinimizeChange(transition, taskId = task.taskId)
@@ -257,6 +269,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun onTransitionReady_pendingTransition_changeTaskToBack_boundsSaved() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val bounds = Rect(0, 0, 200, 200)
val transition = Binder()
val task = setUpFreeformTask()
@@ -280,6 +294,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun onTransitionReady_transitionMergedFromPending_taskIsMinimized() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val mergedTransition = Binder()
val newTransition = Binder()
val task = setUpFreeformTask()
@@ -302,6 +318,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
fun removeLeftoverMinimizedTasks_activeNonMinimizedTasksStillAround_doesNothing() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
desktopTaskRepo.addTask(displayId = DEFAULT_DISPLAY, taskId = 1, isVisible = true)
desktopTaskRepo.addTask(displayId = DEFAULT_DISPLAY, taskId = 2, isVisible = true)
desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2)
@@ -318,6 +336,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
fun removeLeftoverMinimizedTasks_noMinimizedTasks_doesNothing() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val wct = WindowContainerTransaction()
desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks(
DEFAULT_DISPLAY,
@@ -330,6 +350,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
fun removeLeftoverMinimizedTasks_onlyMinimizedTasksLeft_removesAllMinimizedTasks() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task1.taskId)
@@ -351,6 +373,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
fun removeLeftoverMinimizedTasks_onlyMinimizedTasksLeft_backNavEnabled_doesNothing() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task1.taskId)
@@ -364,6 +388,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun addAndGetMinimizeTaskChanges_tasksWithinLimit_noTaskMinimized() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
(1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
val wct = WindowContainerTransaction()
@@ -380,6 +406,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun addAndGetMinimizeTaskChanges_tasksAboveLimit_backTaskMinimized() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
// The following list will be ordered bottom -> top, as the last task is moved to top last.
val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
@@ -399,6 +427,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun addAndGetMinimizeTaskChanges_nonMinimizedTasksWithinLimit_noTaskMinimized() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = tasks[0].taskId)
@@ -416,6 +446,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun getTaskToMinimize_tasksWithinLimit_returnsNull() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
val minimizedTask =
@@ -426,6 +458,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun getTaskToMinimize_tasksAboveLimit_returnsBackTask() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val tasks = (1..MAX_TASK_LIMIT + 1).map { setUpFreeformTask() }
val minimizedTask =
@@ -437,6 +471,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun getTaskToMinimize_tasksAboveLimit_otherLimit_returnsBackTask() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
desktopTasksLimiter =
DesktopTasksLimiter(
transitions,
@@ -458,6 +494,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun getTaskToMinimize_withNewTask_tasksAboveLimit_returnsBackTask() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
val minimizedTask =
@@ -472,6 +510,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun getTaskToMinimize_tasksAtLimit_newIntentReturnsBackTask() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
val minimizedTask =
desktopTasksLimiter.getTaskIdToMinimize(
@@ -486,6 +526,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun minimizeTransitionReadyAndFinished_logsJankInstrumentationBeginAndEnd() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
(1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
val transition = Binder()
val task = setUpFreeformTask()
@@ -510,6 +552,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun minimizeTransitionReadyAndAborted_logsJankInstrumentationBeginAndCancel() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
(1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
val transition = Binder()
val task = setUpFreeformTask()
@@ -534,6 +578,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun minimizeTransitionReadyAndMerged_logsJankInstrumentationBeginAndEnd() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
(1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
val mergedTransition = Binder()
val newTransition = Binder()
@@ -566,6 +612,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun getMinimizingTask_pendingTaskTransition_returnsTask() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val transition = Binder()
val task = setUpFreeformTask()
addPendingMinimizeChange(
@@ -582,6 +630,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun getMinimizingTask_activeTaskTransition_returnsTask() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val transition = Binder()
val task = setUpFreeformTask()
addPendingMinimizeChange(
@@ -613,6 +663,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun getUnminimizingTask_pendingTaskTransition_returnsTask() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val transition = Binder()
val task = setUpFreeformTask()
addPendingUnminimizeChange(
@@ -632,6 +684,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
@Test
fun getUnminimizingTask_activeTaskTransition_returnsTask() {
+ desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+ desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
val transition = Binder()
val task = setUpFreeformTask()
addPendingMinimizeChange(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
index aee8821a63f6..8b6cafb10df4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
@@ -35,6 +35,7 @@ object DesktopTestHelpers {
): RunningTaskInfo =
TestRunningTaskInfoBuilder()
.setDisplayId(displayId)
+ .setParentTaskId(displayId)
.setToken(MockToken().token())
.setActivityType(ACTIVITY_TYPE_STANDARD)
.setWindowingMode(WINDOWING_MODE_FREEFORM)
@@ -73,10 +74,14 @@ object DesktopTestHelpers {
.setLastActiveTime(100)
.build()
+ /**
+ * Create a new System Modal task builder, i.e. a builder for a task with only transparent
+ * activities.
+ */
+ fun createSystemModalTaskBuilder(displayId: Int = DEFAULT_DISPLAY): TestRunningTaskInfoBuilder =
+ createFullscreenTaskBuilder(displayId).setActivityStackTransparent(true).setNumActivities(1)
+
/** Create a new System Modal task, i.e. a task with only transparent activities. */
fun createSystemModalTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo =
- createFullscreenTaskBuilder(displayId)
- .setActivityStackTransparent(true)
- .setNumActivities(1)
- .build()
+ createSystemModalTaskBuilder(displayId).build()
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/OverviewToDesktopTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/OverviewToDesktopTransitionObserverTest.kt
new file mode 100644
index 000000000000..490c42f980e3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/OverviewToDesktopTransitionObserverTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.desktopmode
+
+import android.os.Binder
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.sysui.ShellInit
+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.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.mock
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class OverviewToDesktopTransitionObserverTest : ShellTestCase() {
+
+ @Mock private lateinit var transitions: Transitions
+
+ @Mock private lateinit var moveToDesktopCallback: IMoveToDesktopCallback
+
+ private val testExecutor = mock<ShellExecutor>()
+ private lateinit var shellInit: ShellInit
+ private lateinit var transitionObserver: OverviewToDesktopTransitionObserver
+ private val token = Binder()
+
+ @Before
+ fun setup() {
+ shellInit = spy(ShellInit(testExecutor))
+ transitionObserver = OverviewToDesktopTransitionObserver(transitions, shellInit)
+ }
+
+ @Test
+ fun moveToDesktop_onTransitionEnd_invokesCallback() {
+ transitionObserver.addPendingOverviewTransition(token, moveToDesktopCallback)
+
+ transitionObserver.onTransitionFinished(token, false)
+
+ verify(moveToDesktopCallback).onTaskMovedToDesktop()
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
index 1569f9dc9b10..463ae1d0a80c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.desktopmode.compatui
+import android.content.Intent
import android.os.Binder
import android.testing.AndroidTestingRunner
import android.view.SurfaceControl
@@ -29,7 +30,9 @@ import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTaskBuilder
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createSystemModalTask
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.createSystemModalTaskBuilder
import com.android.wm.shell.desktopmode.DesktopUserRepositories
+import com.android.wm.shell.desktopmode.DesktopWallpaperActivity
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
@@ -116,6 +119,19 @@ class SystemModalsTransitionHandlerTest : ShellTestCase() {
}
@Test
+ fun startAnimation_launchingWallpaperTask_doesNotAnimate() {
+ val wallpaperTask =
+ createSystemModalTaskBuilder().setBaseIntent(createWallpaperIntent()).build()
+ val info =
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_OPEN, wallpaperTask).build()
+
+ assertThat(transitionHandler.startAnimation(Binder(), info, startT, finishT) {}).isFalse()
+ }
+
+ private fun createWallpaperIntent() =
+ Intent().apply { setComponent(DesktopWallpaperActivity.wallpaperActivityComponent) }
+
+ @Test
fun startAnimation_launchingFullscreenTask_doesNotAnimate() {
val info =
TransitionInfoBuilder(TRANSIT_OPEN)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
index 5475032f35a9..493a8c83c48e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
@@ -29,7 +29,6 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.desktopmode.CaptionState
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository
import com.android.wm.shell.desktopmode.education.AppHandleEducationController.Companion.APP_HANDLE_EDUCATION_DELAY_MILLIS
-import com.android.wm.shell.desktopmode.education.AppHandleEducationController.Companion.APP_HANDLE_EDUCATION_TIMEOUT_MILLIS
import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
@@ -47,7 +46,6 @@ import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.Before
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -113,10 +111,10 @@ class AppHandleEducationControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun init_appHandleVisible_shouldCallShowEducationTooltip() =
+ fun init_appHandleVisible_shouldCallShowEducationTooltipAndMarkAsViewed() =
testScope.runTest {
// App handle is visible. Should show education tooltip.
- setShouldShowAppHandleEducation(true)
+ setShouldShowDesktopModeEducation(true)
// Simulate app handle visible.
testCaptionStateFlow.value = createAppHandleState()
@@ -124,6 +122,38 @@ class AppHandleEducationControllerTest : ShellTestCase() {
waitForBufferDelay()
verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
+ verify(mockDataStoreRepository, times(1))
+ .updateAppHandleHintViewedTimestampMillis(eq(true))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ fun init_appHandleVisibleAndMenuExpanded_shouldCallShowEducationTooltipAndMarkAsViewed() =
+ testScope.runTest {
+ setShouldShowDesktopModeEducation(true)
+
+ // Simulate app handle visible and handle menu is expanded.
+ testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
+ waitForBufferDelay()
+
+ verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
+ verify(mockDataStoreRepository, times(1))
+ .updateEnterDesktopModeHintViewedTimestampMillis(eq(true))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+ fun init_appHeaderVisible_shouldCallShowEducationTooltipAndMarkAsViewed() =
+ testScope.runTest {
+ setShouldShowDesktopModeEducation(true)
+
+ // Simulate app header visible.
+ testCaptionStateFlow.value = createAppHeaderState()
+ waitForBufferDelay()
+
+ verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
+ verify(mockDataStoreRepository, times(1))
+ .updateExitDesktopModeHintViewedTimestampMillis(eq(true))
}
@Test
@@ -133,7 +163,7 @@ class AppHandleEducationControllerTest : ShellTestCase() {
// App handle visible but education aconfig flag disabled, should not show education
// tooltip.
whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(false)
- setShouldShowAppHandleEducation(true)
+ setShouldShowDesktopModeEducation(true)
// Simulate app handle visible.
testCaptionStateFlow.value = createAppHandleState()
@@ -145,12 +175,11 @@ class AppHandleEducationControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun init_shouldShowAppHandleEducationReturnsFalse_shouldNotCallShowEducationTooltip() =
+ fun init_shouldShowDesktopModeEducationReturnsFalse_shouldNotCallShowEducationTooltip() =
testScope.runTest {
- // App handle is visible but [shouldShowAppHandleEducation] api returns false, should
- // not
- // show education tooltip.
- setShouldShowAppHandleEducation(false)
+ // App handle is visible but [shouldShowDesktopModeEducation] api returns false, should
+ // not show education tooltip.
+ setShouldShowDesktopModeEducation(false)
// Simulate app handle visible.
testCaptionStateFlow.value = createAppHandleState()
@@ -165,7 +194,7 @@ class AppHandleEducationControllerTest : ShellTestCase() {
fun init_appHandleNotVisible_shouldNotCallShowEducationTooltip() =
testScope.runTest {
// App handle is not visible, should not show education tooltip.
- setShouldShowAppHandleEducation(true)
+ setShouldShowDesktopModeEducation(true)
// Simulate app handle is not visible.
testCaptionStateFlow.value = CaptionState.NoCaption
@@ -184,7 +213,7 @@ class AppHandleEducationControllerTest : ShellTestCase() {
// Mark app handle hint viewed.
testDataStoreFlow.value =
createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L)
- setShouldShowAppHandleEducation(true)
+ setShouldShowDesktopModeEducation(true)
// Simulate app handle visible.
testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
@@ -196,231 +225,95 @@ class AppHandleEducationControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun overridePrerequisite_appHandleHintViewedAlready_shouldCallShowEducationTooltip() =
+ fun init_enterDesktopModeHintViewedAlready_shouldNotCallShowEducationTooltip() =
testScope.runTest {
- // App handle is visible but app handle hint has been viewed before.
- // But as we are overriding prerequisite conditions, we should show app
- // handle tooltip.
+ // App handle is visible but app handle hint has been viewed before,
+ // should not show education tooltip.
// Mark app handle hint viewed.
testDataStoreFlow.value =
- createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L)
- val systemPropertiesKey =
- "persist.desktop_windowing_app_handle_education_override_conditions"
- whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean()))
- .thenReturn(true)
- setShouldShowAppHandleEducation(true)
+ createWindowingEducationProto(enterDesktopModeHintViewedTimestampMillis = 123L)
+ setShouldShowDesktopModeEducation(true)
// Simulate app handle visible.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
- // Wait for first tooltip to showup.
- waitForBufferDelay()
-
- verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun init_appHandleExpanded_shouldMarkAppHandleHintUsed() =
- testScope.runTest {
- setShouldShowAppHandleEducation(false)
-
- // Simulate app handle visible and expanded.
testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
- // Wait for some time before verifying
+ // Wait for first tooltip to showup.
waitForBufferDelay()
- verify(mockDataStoreRepository, times(1))
- .updateAppHandleHintUsedTimestampMillis(eq(true))
+ verify(mockTooltipController, never()).showEducationTooltip(any(), any())
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun init_showFirstTooltip_shouldMarkAppHandleHintViewed() =
+ fun init_exitDesktopModeHintViewedAlready_shouldNotCallShowEducationTooltip() =
testScope.runTest {
- // App handle is visible. Should show education tooltip.
- setShouldShowAppHandleEducation(true)
+ // App handle is visible but app handle hint has been viewed before,
+ // should not show education tooltip.
+ // Mark app handle hint viewed.
+ testDataStoreFlow.value =
+ createWindowingEducationProto(exitDesktopModeHintViewedTimestampMillis = 123L)
+ setShouldShowDesktopModeEducation(true)
// Simulate app handle visible.
- testCaptionStateFlow.value = createAppHandleState()
+ testCaptionStateFlow.value = createAppHeaderState()
// Wait for first tooltip to showup.
waitForBufferDelay()
- verify(mockDataStoreRepository, times(1))
- .updateAppHandleHintViewedTimestampMillis(eq(true))
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showWindowingImageButtonTooltip_appHandleExpanded_shouldCallShowEducationTooltipTwice() =
- testScope.runTest {
- // After first tooltip is dismissed, app handle is expanded. Should show second
- // education
- // tooltip.
- showAndDismissFirstTooltip()
-
- // Simulate app handle expanded.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
- // Wait for next tooltip to showup.
- waitForBufferDelay()
-
- // [showEducationTooltip] should be called twice, once for each tooltip.
- verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showWindowingImageButtonTooltip_appHandleExpandedAfterTimeout_shouldCallShowEducationTooltipOnce() =
- testScope.runTest {
- // After first tooltip is dismissed, app handle is expanded after timeout. Should not
- // show
- // second education tooltip.
- showAndDismissFirstTooltip()
-
- // Wait for timeout to occur, after this timeout we should not listen for further
- // triggers
- // anymore.
- advanceTimeBy(APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS)
- runCurrent()
- // Simulate app handle expanded.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
- // Wait for next tooltip to showup.
- waitForBufferDelay()
-
- // [showEducationTooltip] should be called once, just for the first tooltip.
- verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showWindowingImageButtonTooltip_appHandleExpandedTwice_shouldCallShowEducationTooltipTwice() =
- testScope.runTest {
- // After first tooltip is dismissed, app handle is expanded twice. Should show second
- // education tooltip only once.
- showAndDismissFirstTooltip()
-
- // Simulate app handle expanded.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
- // Wait for next tooltip to showup.
- waitForBufferDelay()
- // Simulate app handle being expanded twice.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
- waitForBufferDelay()
-
- // [showEducationTooltip] should not be called thrice, even if app handle was expanded
- // twice. Should be called twice, once for each tooltip.
- verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
+ verify(mockTooltipController, never()).showEducationTooltip(any(), any())
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showWindowingImageButtonTooltip_appHandleNotExpanded_shouldCallShowEducationTooltipOnce() =
+ fun overridePrerequisite_appHandleHintViewedAlready_shouldCallShowEducationTooltip() =
testScope.runTest {
- // After first tooltip is dismissed, app handle is not expanded. Should not show second
- // education tooltip.
- showAndDismissFirstTooltip()
+ // App handle is visible but app handle hint has been viewed before.
+ // But as we are overriding prerequisite conditions, we should show app
+ // handle tooltip.
+ // Mark app handle hint viewed.
+ testDataStoreFlow.value =
+ createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L)
+ val systemPropertiesKey = "persist.windowing_force_show_desktop_mode_education"
+ whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean()))
+ .thenReturn(true)
+ setShouldShowDesktopModeEducation(true)
- // Simulate app handle visible but not expanded.
+ // Simulate app handle visible.
testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
- // Wait for next tooltip to showup.
+ // Wait for first tooltip to showup.
waitForBufferDelay()
- // [showEducationTooltip] should be called once, just for the first tooltip.
verify(mockTooltipController, times(1)).showEducationTooltip(any(), any())
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showExitWindowingButtonTooltip_appHeaderVisible_shouldCallShowEducationTooltipThrice() =
- testScope.runTest {
- // After first two tooltips are dismissed, app header is visible. Should show third
- // education tooltip.
- showAndDismissFirstTooltip()
- showAndDismissSecondTooltip()
-
- // Simulate app header visible.
- testCaptionStateFlow.value = createAppHeaderState()
- // Wait for next tooltip to showup.
- waitForBufferDelay()
-
- verify(mockTooltipController, times(3)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showExitWindowingButtonTooltip_appHeaderVisibleAfterTimeout_shouldCallShowEducationTooltipTwice() =
- testScope.runTest {
- // After first two tooltips are dismissed, app header is visible after timeout. Should
- // not
- // show third education tooltip.
- showAndDismissFirstTooltip()
- showAndDismissSecondTooltip()
-
- // Wait for timeout to occur, after this timeout we should not listen for further
- // triggers
- // anymore.
- advanceTimeBy(APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS)
- runCurrent()
- // Simulate app header visible.
- testCaptionStateFlow.value = createAppHeaderState()
- // Wait for next tooltip to showup.
- waitForBufferDelay()
-
- verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showExitWindowingButtonTooltip_appHeaderVisibleTwice_shouldCallShowEducationTooltipThrice() =
+ fun clickAppHandleHint_openHandleMenuCallbackInvoked() =
testScope.runTest {
- // After first two tooltips are dismissed, app header is visible twice. Should show
- // third
- // education tooltip only once.
- showAndDismissFirstTooltip()
- showAndDismissSecondTooltip()
-
- // Simulate app header visible.
- testCaptionStateFlow.value = createAppHeaderState()
- // Wait for next tooltip to showup.
- waitForBufferDelay()
- testCaptionStateFlow.value = createAppHeaderState()
- // Wait for next tooltip to showup.
+ // App handle is visible. Should show education tooltip.
+ setShouldShowDesktopModeEducation(true)
+ val mockOpenHandleMenuCallback: (Int) -> Unit = mock()
+ val mockToDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit = mock()
+ educationController.setAppHandleEducationTooltipCallbacks(
+ mockOpenHandleMenuCallback,
+ mockToDesktopModeCallback,
+ )
+ // Simulate app handle visible.
+ testCaptionStateFlow.value = createAppHandleState()
+ // Wait for first tooltip to showup.
waitForBufferDelay()
- verify(mockTooltipController, times(3)).showEducationTooltip(any(), any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun showExitWindowingButtonTooltip_appHeaderExpanded_shouldCallShowEducationTooltipTwice() =
- testScope.runTest {
- // After first two tooltips are dismissed, app header is visible but expanded. Should
- // not
- // show third education tooltip.
- showAndDismissFirstTooltip()
- showAndDismissSecondTooltip()
-
- // Simulate app header visible.
- testCaptionStateFlow.value = createAppHeaderState(isHeaderMenuExpanded = true)
- // Wait for next tooltip to showup.
- waitForBufferDelay()
+ verify(mockTooltipController, atLeastOnce())
+ .showEducationTooltip(educationConfigCaptor.capture(), any())
+ educationConfigCaptor.lastValue.onEducationClickAction.invoke()
- verify(mockTooltipController, times(2)).showEducationTooltip(any(), any())
+ verify(mockOpenHandleMenuCallback, times(1)).invoke(any())
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- fun setAppHandleEducationTooltipCallbacks_onAppHandleTooltipClicked_callbackInvoked() =
+ fun clickEnterDesktopModeHint_toDesktopModeCallbackInvoked() =
testScope.runTest {
// App handle is visible. Should show education tooltip.
- setShouldShowAppHandleEducation(true)
+ setShouldShowDesktopModeEducation(true)
val mockOpenHandleMenuCallback: (Int) -> Unit = mock()
val mockToDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit = mock()
educationController.setAppHandleEducationTooltipCallbacks(
@@ -428,7 +321,7 @@ class AppHandleEducationControllerTest : ShellTestCase() {
mockToDesktopModeCallback,
)
// Simulate app handle visible.
- testCaptionStateFlow.value = createAppHandleState()
+ testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
// Wait for first tooltip to showup.
waitForBufferDelay()
@@ -436,68 +329,41 @@ class AppHandleEducationControllerTest : ShellTestCase() {
.showEducationTooltip(educationConfigCaptor.capture(), any())
educationConfigCaptor.lastValue.onEducationClickAction.invoke()
- verify(mockOpenHandleMenuCallback, times(1)).invoke(any())
+ verify(mockToDesktopModeCallback, times(1))
+ .invoke(any(), eq(DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON))
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
- @Ignore("b/371527084: revisit testcase after refactoring original logic")
- fun setAppHandleEducationTooltipCallbacks_onWindowingImageButtonTooltipClicked_callbackInvoked() =
+ fun clickExitDesktopModeHint_openHandleMenuCallbackInvoked() =
testScope.runTest {
- // After first tooltip is dismissed, app handle is expanded. Should show second
- // education
- // tooltip.
- showAndDismissFirstTooltip()
+ // App handle is visible. Should show education tooltip.
+ setShouldShowDesktopModeEducation(true)
val mockOpenHandleMenuCallback: (Int) -> Unit = mock()
val mockToDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit = mock()
educationController.setAppHandleEducationTooltipCallbacks(
mockOpenHandleMenuCallback,
mockToDesktopModeCallback,
)
- // Simulate app handle expanded.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
- // Wait for next tooltip to showup.
+ // Simulate app handle visible.
+ testCaptionStateFlow.value = createAppHeaderState()
+ // Wait for first tooltip to showup.
waitForBufferDelay()
verify(mockTooltipController, atLeastOnce())
.showEducationTooltip(educationConfigCaptor.capture(), any())
educationConfigCaptor.lastValue.onEducationClickAction.invoke()
- verify(mockToDesktopModeCallback, times(1)).invoke(any(), any())
+ verify(mockOpenHandleMenuCallback, times(1)).invoke(any())
}
- private suspend fun TestScope.showAndDismissFirstTooltip() {
- setShouldShowAppHandleEducation(true)
- // Simulate app handle visible.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false)
- // Wait for first tooltip to showup.
- waitForBufferDelay()
- // [shouldShowAppHandleEducation] should return false as education has been viewed
- // before.
- setShouldShowAppHandleEducation(false)
- // Dismiss previous tooltip, after this we should listen for next tooltip's trigger.
- captureAndInvokeOnDismissAction()
+ private suspend fun setShouldShowDesktopModeEducation(shouldShowDesktopModeEducation: Boolean) {
+ whenever(mockEducationFilter.shouldShowDesktopModeEducation(any<CaptionState.AppHandle>()))
+ .thenReturn(shouldShowDesktopModeEducation)
+ whenever(mockEducationFilter.shouldShowDesktopModeEducation(any<CaptionState.AppHeader>()))
+ .thenReturn(shouldShowDesktopModeEducation)
}
- private fun TestScope.showAndDismissSecondTooltip() {
- // Simulate app handle expanded.
- testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = true)
- // Wait for next tooltip to showup.
- waitForBufferDelay()
- // Dismiss previous tooltip, after this we should listen for next tooltip's trigger.
- captureAndInvokeOnDismissAction()
- }
-
- private fun captureAndInvokeOnDismissAction() {
- verify(mockTooltipController, atLeastOnce())
- .showEducationTooltip(educationConfigCaptor.capture(), any())
- educationConfigCaptor.lastValue.onDismissAction.invoke()
- }
-
- private suspend fun setShouldShowAppHandleEducation(shouldShowAppHandleEducation: Boolean) =
- whenever(mockEducationFilter.shouldShowAppHandleEducation(any()))
- .thenReturn(shouldShowAppHandleEducation)
-
/**
* Class under test waits for some time before showing education, simulate advance time before
* verifying or moving forward
@@ -510,7 +376,5 @@ class AppHandleEducationControllerTest : ShellTestCase() {
private companion object {
val APP_HANDLE_EDUCATION_DELAY_BUFFER_MILLIS: Long =
APP_HANDLE_EDUCATION_DELAY_MILLIS + 1000L
- val APP_HANDLE_EDUCATION_TIMEOUT_BUFFER_MILLIS: Long =
- APP_HANDLE_EDUCATION_TIMEOUT_MILLIS + 1000L
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
index 4db883d13551..31dfc78902b2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
@@ -123,6 +123,24 @@ class AppHandleEducationDatastoreRepositoryTest {
}
@Test
+ fun updateEnterDesktopModeHintViewedTimestampMillis_updatesDatastoreProto() =
+ runTest(StandardTestDispatcher()) {
+ datastoreRepository.updateEnterDesktopModeHintViewedTimestampMillis(true)
+
+ val result = testDatastore.data.first().hasEnterDesktopModeHintViewedTimestampMillis()
+ assertThat(result).isEqualTo(true)
+ }
+
+ @Test
+ fun updateExitDesktopModeHintViewedTimestampMillis_updatesDatastoreProto() =
+ runTest(StandardTestDispatcher()) {
+ datastoreRepository.updateExitDesktopModeHintViewedTimestampMillis(true)
+
+ val result = testDatastore.data.first().hasExitDesktopModeHintViewedTimestampMillis()
+ assertThat(result).isEqualTo(true)
+ }
+
+ @Test
fun updateAppHandleHintUsedTimestampMillis_updatesDatastoreProto() =
runTest(StandardTestDispatcher()) {
datastoreRepository.updateAppHandleHintUsedTimestampMillis(true)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt
index 2fc36efb1a41..218226240c0f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt
@@ -89,9 +89,9 @@ class AppHandleEducationFilterTest : ShellTestCase() {
}
@Test
- fun shouldShowAppHandleEducation_isTriggerValid_returnsTrue() = runTest {
- // setup() makes sure that all of the conditions satisfy and #shouldShowAppHandleEducation
- // should return true
+ fun shouldShowDesktopModeEducation_isTriggerValid_returnsTrue() = runTest {
+ // setup() makes sure that all of the conditions satisfy and
+ // [shouldShowDesktopModeEducation] should return true
val windowingEducationProto =
createWindowingEducationProto(
appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
@@ -99,16 +99,15 @@ class AppHandleEducationFilterTest : ShellTestCase() {
)
`when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
- val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+ val result = educationFilter.shouldShowDesktopModeEducation(createAppHandleState())
assertThat(result).isTrue()
}
@Test
- fun shouldShowAppHandleEducation_focusAppNotInAllowlist_returnsFalse() = runTest {
+ fun shouldShowDesktopModeEducation_focusAppNotInAllowlist_returnsFalse() = runTest {
// Pass Youtube as current focus app, it is not in allowlist hence
- // #shouldShowAppHandleEducation
- // should return false
+ // [shouldShowDesktopModeEducation] should return false
testableResources.addOverride(
R.array.desktop_windowing_app_handle_education_allowlist_apps,
arrayOf(GMAIL_PACKAGE_NAME),
@@ -122,16 +121,15 @@ class AppHandleEducationFilterTest : ShellTestCase() {
createAppHandleState(createTaskInfo(runningTaskPackageName = YOUTUBE_PACKAGE_NAME))
`when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
- val result = educationFilter.shouldShowAppHandleEducation(captionState)
+ val result = educationFilter.shouldShowDesktopModeEducation(captionState)
assertThat(result).isFalse()
}
@Test
- fun shouldShowAppHandleEducation_timeSinceSetupIsNotSufficient_returnsFalse() = runTest {
- // Time required to have passed setup is > 100 years, hence #shouldShowAppHandleEducation
- // should
- // return false
+ fun shouldShowDesktopModeEducation_timeSinceSetupIsNotSufficient_returnsFalse() = runTest {
+ // Time required to have passed setup is > 100 years, hence [shouldShowDesktopModeEducation]
+ // should return false
testableResources.addOverride(
R.integer.desktop_windowing_education_required_time_since_setup_seconds,
MAX_VALUE,
@@ -143,50 +141,15 @@ class AppHandleEducationFilterTest : ShellTestCase() {
)
`when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
- val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
-
- assertThat(result).isFalse()
- }
-
- @Test
- fun shouldShowAppHandleEducation_appHandleHintViewedBefore_returnsFalse() = runTest {
- // App handle hint has been viewed before, hence #shouldShowAppHandleEducation should return
- // false
- val windowingEducationProto =
- createWindowingEducationProto(
- appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
- appHandleHintViewedTimestampMillis = 123L,
- appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
- )
- `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
- val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
-
- assertThat(result).isFalse()
- }
-
- @Test
- fun shouldShowAppHandleEducation_appHandleHintUsedBefore_returnsFalse() = runTest {
- // App handle hint has been used before, hence #shouldShowAppHandleEducation should return
- // false
- val windowingEducationProto =
- createWindowingEducationProto(
- appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
- appHandleHintUsedTimestampMillis = 123L,
- appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
- )
- `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
- val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+ val result = educationFilter.shouldShowDesktopModeEducation(createAppHandleState())
assertThat(result).isFalse()
}
@Test
- fun shouldShowAppHandleEducation_doesNotHaveMinAppUsage_returnsFalse() = runTest {
+ fun shouldShowDesktopModeEducation_doesNotHaveMinAppUsage_returnsFalse() = runTest {
// Simulate that gmail app has been launched twice before, minimum app launch count is 3,
- // hence
- // #shouldShowAppHandleEducation should return false
+ // hence [shouldShowDesktopModeEducation] should return false
testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
val windowingEducationProto =
createWindowingEducationProto(
@@ -195,13 +158,13 @@ class AppHandleEducationFilterTest : ShellTestCase() {
)
`when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
- val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+ val result = educationFilter.shouldShowDesktopModeEducation(createAppHandleState())
assertThat(result).isFalse()
}
@Test
- fun shouldShowAppHandleEducation_appUsageStatsStale_queryAppUsageStats() = runTest {
+ fun shouldShowDesktopModeEducation_appUsageStatsStale_queryAppUsageStats() = runTest {
// UsageStats caching interval is set to 0ms, that means caching should happen very
// frequently
testableResources.addOverride(
@@ -209,8 +172,7 @@ class AppHandleEducationFilterTest : ShellTestCase() {
0,
)
// The DataStore currently holds a proto object where Gmail's app launch count is recorded
- // as 4.
- // This value exceeds the minimum required count of 3.
+ // as 4. This value exceeds the minimum required count of 3.
testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
val windowingEducationProto =
createWindowingEducationProto(
@@ -223,40 +185,20 @@ class AppHandleEducationFilterTest : ShellTestCase() {
.thenReturn(mapOf(GMAIL_PACKAGE_NAME to UsageStats().apply { mAppLaunchCount = 2 }))
`when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
- val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+ val result = educationFilter.shouldShowDesktopModeEducation(createAppHandleState())
// Result should be false as queried usage stats should be considered to determine the
- // result
- // instead of cached stats
- assertThat(result).isFalse()
- }
-
- @Test
- fun shouldShowAppHandleEducation_appHandleMenuExpanded_returnsFalse() = runTest {
- val windowingEducationProto =
- createWindowingEducationProto(
- appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
- appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE,
- )
- // Simulate app handle menu is expanded
- val captionState = createAppHandleState(isHandleMenuExpanded = true)
- `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
-
- val result = educationFilter.shouldShowAppHandleEducation(captionState)
-
- // We should not show app handle education if app menu is expanded
+ // result instead of cached stats
assertThat(result).isFalse()
}
@Test
- fun shouldShowAppHandleEducation_overridePrerequisite_returnsTrue() = runTest {
+ fun shouldShowDesktopModeEducation_overridePrerequisite_returnsTrue() = runTest {
// Simulate that gmail app has been launched twice before, minimum app launch count is 3,
- // hence
- // #shouldShowAppHandleEducation should return false. But as we are overriding prerequisite
- // conditions, #shouldShowAppHandleEducation should return true.
+ // hence [shouldShowDesktopModeEducation] should return false. But as we are overriding
+ // prerequisite conditions, [shouldShowDesktopModeEducation] should return true.
testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
- val systemPropertiesKey =
- "persist.desktop_windowing_app_handle_education_override_conditions"
+ val systemPropertiesKey = "persist.windowing_force_show_desktop_mode_education"
whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean()))
.thenReturn(true)
val windowingEducationProto =
@@ -266,7 +208,7 @@ class AppHandleEducationFilterTest : ShellTestCase() {
)
`when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
- val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState())
+ val result = educationFilter.shouldShowDesktopModeEducation(createAppHandleState())
assertThat(result).isTrue()
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
new file mode 100644
index 000000000000..a07203d86b75
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.desktopmode.multidesks
+
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.SurfaceControl
+import android.window.TransitionInfo
+import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.HierarchyOp
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
+import com.android.wm.shell.sysui.ShellCommandHandler
+import com.android.wm.shell.sysui.ShellInit
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+/**
+ * Tests for [RootTaskDesksOrganizer].
+ *
+ * Usage: atest WMShellUnitTests:RootTaskDesksOrganizerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class RootTaskDesksOrganizerTest : ShellTestCase() {
+
+ private val testExecutor = TestShellExecutor()
+ private val testShellInit = ShellInit(testExecutor)
+ private val mockShellCommandHandler = mock<ShellCommandHandler>()
+ private val mockShellTaskOrganizer = mock<ShellTaskOrganizer>()
+
+ private lateinit var organizer: RootTaskDesksOrganizer
+
+ @Before
+ fun setUp() {
+ organizer =
+ RootTaskDesksOrganizer(testShellInit, mockShellCommandHandler, mockShellTaskOrganizer)
+ }
+
+ @Test
+ fun testCreateDesk_callsBack() {
+ val callback = FakeOnCreateCallback()
+ organizer.createDesk(Display.DEFAULT_DISPLAY, callback)
+
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+
+ assertThat(callback.created).isTrue()
+ assertEquals(freeformRoot.taskId, callback.deskId)
+ }
+
+ @Test
+ fun testOnTaskAppeared_withoutRequest_throws() {
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+
+ assertThrows(Exception::class.java) {
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+ }
+ }
+
+ @Test
+ fun testOnTaskAppeared_withRequestOnlyInAnotherDisplay_throws() {
+ organizer.createDesk(displayId = 2, FakeOnCreateCallback())
+ val freeformRoot = createFreeformTask(Display.DEFAULT_DISPLAY).apply { parentTaskId = -1 }
+
+ assertThrows(Exception::class.java) {
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+ }
+ }
+
+ @Test
+ fun testOnTaskAppeared_duplicateRoot_throws() {
+ organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+
+ assertThrows(Exception::class.java) {
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+ }
+ }
+
+ @Test
+ fun testOnTaskVanished_removesRoot() {
+ val callback = FakeOnCreateCallback()
+ organizer.createDesk(Display.DEFAULT_DISPLAY, callback)
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+
+ organizer.onTaskVanished(freeformRoot)
+
+ assertThat(organizer.roots.contains(freeformRoot.taskId)).isFalse()
+ }
+
+ @Test
+ fun testDesktopWindowAppearsInDesk() {
+ organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+ val child = createFreeformTask().apply { parentTaskId = freeformRoot.taskId }
+
+ organizer.onTaskAppeared(child, SurfaceControl())
+
+ assertThat(organizer.roots[freeformRoot.taskId].children).contains(child.taskId)
+ }
+
+ @Test
+ fun testDesktopWindowDisappearsFromDesk() {
+ organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+ val child = createFreeformTask().apply { parentTaskId = freeformRoot.taskId }
+
+ organizer.onTaskAppeared(child, SurfaceControl())
+ organizer.onTaskVanished(child)
+
+ assertThat(organizer.roots[freeformRoot.taskId].children).doesNotContain(child.taskId)
+ }
+
+ @Test
+ fun testRemoveDesk() {
+ organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+
+ val wct = WindowContainerTransaction()
+ organizer.removeDesk(wct, freeformRoot.taskId)
+
+ assertThat(
+ wct.hierarchyOps.any { hop ->
+ hop.type == HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK &&
+ hop.container == freeformRoot.token.asBinder()
+ }
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun testRemoveDesk_didNotExist_throws() {
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+
+ val wct = WindowContainerTransaction()
+ assertThrows(Exception::class.java) { organizer.removeDesk(wct, freeformRoot.taskId) }
+ }
+
+ @Test
+ fun testActivateDesk() {
+ organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+
+ val wct = WindowContainerTransaction()
+ organizer.activateDesk(wct, freeformRoot.taskId)
+
+ assertThat(
+ wct.hierarchyOps.any { hop ->
+ hop.type == HierarchyOp.HIERARCHY_OP_TYPE_REORDER &&
+ hop.toTop &&
+ hop.container == freeformRoot.token.asBinder()
+ }
+ )
+ .isTrue()
+ assertThat(
+ wct.hierarchyOps.any { hop ->
+ hop.type == HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT &&
+ hop.container == freeformRoot.token.asBinder()
+ }
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun testActivateDesk_didNotExist_throws() {
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+
+ val wct = WindowContainerTransaction()
+ assertThrows(Exception::class.java) { organizer.activateDesk(wct, freeformRoot.taskId) }
+ }
+
+ @Test
+ fun testMoveTaskToDesk() {
+ organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+
+ val desktopTask = createFreeformTask().apply { parentTaskId = -1 }
+ val wct = WindowContainerTransaction()
+ organizer.moveTaskToDesk(wct, freeformRoot.taskId, desktopTask)
+
+ assertThat(
+ wct.hierarchyOps.any { hop ->
+ hop.isReparent &&
+ hop.toTop &&
+ hop.container == desktopTask.token.asBinder() &&
+ hop.newParent == freeformRoot.token.asBinder()
+ }
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun testMoveTaskToDesk_didNotExist_throws() {
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+
+ val desktopTask = createFreeformTask().apply { parentTaskId = -1 }
+ val wct = WindowContainerTransaction()
+ assertThrows(Exception::class.java) {
+ organizer.moveTaskToDesk(wct, freeformRoot.taskId, desktopTask)
+ }
+ }
+
+ @Test
+ fun testGetDeskAtEnd() {
+ organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+
+ val task = createFreeformTask().apply { parentTaskId = freeformRoot.taskId }
+ val endDesk =
+ organizer.getDeskAtEnd(
+ TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task }
+ )
+
+ assertThat(endDesk).isEqualTo(freeformRoot.taskId)
+ }
+
+ private class FakeOnCreateCallback : DesksOrganizer.OnCreateCallback {
+ var deskId: Int? = null
+ val created: Boolean
+ get() = deskId != null
+
+ override fun onCreated(deskId: Int) {
+ this.deskId = deskId
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
index a3c441698905..9a8f264e98a4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.desktopmode.persistence
import android.os.UserManager
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
@@ -24,6 +25,7 @@ import android.view.Display.DEFAULT_DISPLAY
import androidx.test.filters.SmallTest
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_HSUM
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE
+import com.android.window.flags.Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopUserRepositories
@@ -85,7 +87,9 @@ class DesktopRepositoryInitializerTest : ShellTestCase() {
@Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE, FLAG_ENABLE_DESKTOP_WINDOWING_HSUM)
- fun initWithPersistence_multipleUsers_addedCorrectly() =
+ /** TODO: b/362720497 - add multi-desk version when implemented. */
+ @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun initWithPersistence_multipleUsers_addedCorrectly_multiDesksDisabled() =
runTest(StandardTestDispatcher()) {
whenever(persistentRepository.getUserDesktopRepositoryMap())
.thenReturn(
@@ -145,7 +149,9 @@ class DesktopRepositoryInitializerTest : ShellTestCase() {
@Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
- fun initWithPersistence_singleUser_addedCorrectly() =
+ /** TODO: b/362720497 - add multi-desk version when implemented. */
+ @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun initWithPersistence_singleUser_addedCorrectly_multiDesksDisabled() =
runTest(StandardTestDispatcher()) {
whenever(persistentRepository.getUserDesktopRepositoryMap())
.thenReturn(mapOf(USER_ID_1 to desktopRepositoryState1))
@@ -156,24 +162,24 @@ class DesktopRepositoryInitializerTest : ShellTestCase() {
repositoryInitializer.initialize(desktopUserRepositories)
- // Desktop Repository currently returns all tasks across desktops for a specific user
- // since the repository currently doesn't handle desktops. This test logic should be
- // updated
- // once the repository handles multiple desktops.
assertThat(
- desktopUserRepositories.getProfile(USER_ID_1).getActiveTasks(DEFAULT_DISPLAY)
+ desktopUserRepositories
+ .getProfile(USER_ID_1)
+ .getActiveTaskIdsInDesk(deskId = DEFAULT_DISPLAY)
)
.containsExactly(1, 3, 4, 5)
.inOrder()
assertThat(
desktopUserRepositories
.getProfile(USER_ID_1)
- .getExpandedTasksOrdered(DEFAULT_DISPLAY)
+ .getExpandedTasksIdsInDeskOrdered(deskId = DEFAULT_DISPLAY)
)
.containsExactly(5, 1)
.inOrder()
assertThat(
- desktopUserRepositories.getProfile(USER_ID_1).getMinimizedTasks(DEFAULT_DISPLAY)
+ desktopUserRepositories
+ .getProfile(USER_ID_1)
+ .getMinimizedTaskIdsInDesk(deskId = DEFAULT_DISPLAY)
)
.containsExactly(3, 4)
.inOrder()
@@ -195,6 +201,7 @@ class DesktopRepositoryInitializerTest : ShellTestCase() {
val desktop1: Desktop =
Desktop.newBuilder()
.setDesktopId(DESKTOP_ID_1)
+ .setDisplayId(DEFAULT_DISPLAY)
.addAllZOrderedTasks(freeformTasksInZOrder1)
.putTasksByTaskId(
1,
@@ -216,6 +223,7 @@ class DesktopRepositoryInitializerTest : ShellTestCase() {
val desktop2: Desktop =
Desktop.newBuilder()
.setDesktopId(DESKTOP_ID_2)
+ .setDisplayId(DEFAULT_DISPLAY)
.addAllZOrderedTasks(freeformTasksInZOrder2)
.putTasksByTaskId(
4,
@@ -237,6 +245,7 @@ class DesktopRepositoryInitializerTest : ShellTestCase() {
val desktop3: Desktop =
Desktop.newBuilder()
.setDesktopId(DESKTOP_ID_3)
+ .setDisplayId(DEFAULT_DISPLAY)
.addAllZOrderedTasks(freeformTasksInZOrder3)
.putTasksByTaskId(
7,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index fa5989a3ca99..4174bbd89b76 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -46,6 +46,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
@@ -89,6 +90,8 @@ public final class FreeformTaskListenerTests extends ShellTestCase {
@Mock
private DesktopTasksController mDesktopTasksController;
@Mock
+ private DesktopModeLoggerTransitionObserver mDesktopModeLoggerTransitionObserver;
+ @Mock
private LaunchAdjacentController mLaunchAdjacentController;
@Mock
private TaskChangeListener mTaskChangeListener;
@@ -114,6 +117,7 @@ public final class FreeformTaskListenerTests extends ShellTestCase {
mTaskOrganizer,
Optional.of(mDesktopUserRepositories),
Optional.of(mDesktopTasksController),
+ mDesktopModeLoggerTransitionObserver,
mLaunchAdjacentController,
mWindowDecorViewModel,
Optional.of(mTaskChangeListener));
@@ -159,7 +163,8 @@ public final class FreeformTaskListenerTests extends ShellTestCase {
}
@Test
- public void focusTaskChanged_addsFreeformTaskToRepo() {
+ @DisableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+ public void focusTaskChanged_noTransitionObserversFlag_addsFreeformTaskToRepo() {
ActivityManager.RunningTaskInfo task =
new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
task.isFocused = true;
@@ -171,6 +176,19 @@ public final class FreeformTaskListenerTests extends ShellTestCase {
}
@Test
+ @EnableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
+ public void focusTaskChanged_enableTransitionObservers_freeformTaskNotAddedToRepo() {
+ ActivityManager.RunningTaskInfo task =
+ new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ task.isFocused = true;
+
+ mFreeformTaskListener.onFocusTaskChanged(task);
+
+ verify(mDesktopUserRepositories.getCurrent(), never())
+ .addTask(task.displayId, task.taskId, task.isVisible);
+ }
+
+ @Test
public void focusTaskChanged_fullscreenTaskNotAddedToRepo() {
ActivityManager.RunningTaskInfo fullscreenTask =
new TestRunningTaskInfoBuilder()
@@ -269,6 +287,19 @@ public final class FreeformTaskListenerTests extends ShellTestCase {
}
@Test
+ public void onTaskVanished_withDesktopModeLogger_forwards() {
+ ActivityManager.RunningTaskInfo task =
+ new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ task.isVisible = true;
+ mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+ mFreeformTaskListener.onTaskVanished(task);
+
+ verify(mDesktopModeLoggerTransitionObserver).onTaskVanished(task);
+ }
+
+
+ @Test
public void onTaskInfoChanged_withDesktopController_forwards() {
ActivityManager.RunningTaskInfo task =
new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
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 53f6cda62f55..7e68b68e469a 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
@@ -87,7 +87,7 @@ public class PipAnimationControllerTest extends ShellTestCase {
.getAnimator(mTaskInfo, mLeash, new Rect(), 0f, 1f);
assertEquals("Expect ANIM_TYPE_ALPHA animation",
- animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA);
+ animator.getAnimationType(), PipTransitionController.ANIM_TYPE_ALPHA);
}
@Test
@@ -101,7 +101,7 @@ public class PipAnimationControllerTest extends ShellTestCase {
false /* alwaysAnimateTaskBounds */);
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
- animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
+ animator.getAnimationType(), PipTransitionController.ANIM_TYPE_BOUNDS);
}
@Test
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 2eb2c3b8e2f7..836f4c24a979 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
@@ -289,7 +289,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
DisplayLayout layout = new DisplayLayout(info,
mContext.getResources(), true, true);
mPipDisplayLayoutState.setDisplayLayout(layout);
- doReturn(PipAnimationController.ANIM_TYPE_ALPHA).when(mMockPipAnimationController)
+ doReturn(PipTransitionController.ANIM_TYPE_ALPHA).when(mMockPipAnimationController)
.takeOneShotEnterAnimationType();
mPipTaskOrganizer.setSurfaceControlTransactionFactory(
MockSurfaceControlHelper::createMockSurfaceControlTransaction);
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 065fa219e8d0..542289db6cfc 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
@@ -211,6 +211,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testAddRemoveSplitNotifyChange() {
+ reset(mRecentTasksController);
RecentTaskInfo t1 = makeTaskInfo(1);
RecentTaskInfo t2 = makeTaskInfo(2);
setRawList(t1, t2);
@@ -225,6 +226,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testAddSameSplitBoundsInfoSkipNotifyChange() {
+ reset(mRecentTasksController);
RecentTaskInfo t1 = makeTaskInfo(1);
RecentTaskInfo t2 = makeTaskInfo(2);
setRawList(t1, t2);
@@ -535,6 +537,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testTaskWindowingModeChangedNotifiesChange() {
+ reset(mRecentTasksController);
RecentTaskInfo t1 = makeTaskInfo(1);
setRawList(t1);
@@ -551,7 +554,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
WINDOWING_MODE_MULTI_WINDOW);
mShellTaskOrganizer.onTaskInfoChanged(rt2MultiWIndow);
- verify(mRecentTasksController).notifyRecentTasksChanged();
+ // One for onTaskAppeared and one for onTaskInfoChanged
+ verify(mRecentTasksController, times(2)).notifyRecentTasksChanged();
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt
index b9d91e7895db..546848421302 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt
@@ -81,7 +81,9 @@ fun createWindowingEducationProto(
appHandleHintViewedTimestampMillis: Long? = null,
appHandleHintUsedTimestampMillis: Long? = null,
appUsageStats: Map<String, Int>? = null,
- appUsageStatsLastUpdateTimestampMillis: Long? = null
+ appUsageStatsLastUpdateTimestampMillis: Long? = null,
+ enterDesktopModeHintViewedTimestampMillis: Long? = null,
+ exitDesktopModeHintViewedTimestampMillis: Long? = null,
): WindowingEducationProto =
WindowingEducationProto.newBuilder()
.apply {
@@ -91,6 +93,12 @@ fun createWindowingEducationProto(
if (appHandleHintUsedTimestampMillis != null) {
setAppHandleHintUsedTimestampMillis(appHandleHintUsedTimestampMillis)
}
+ if (enterDesktopModeHintViewedTimestampMillis != null) {
+ setEnterDesktopModeHintViewedTimestampMillis(enterDesktopModeHintViewedTimestampMillis)
+ }
+ if (exitDesktopModeHintViewedTimestampMillis != null) {
+ setExitDesktopModeHintViewedTimestampMillis(exitDesktopModeHintViewedTimestampMillis)
+ }
setAppHandleEducation(
createAppHandleEducationProto(appUsageStats, appUsageStatsLastUpdateTimestampMillis))
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index b4791642663a..baccbee0893d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -642,6 +642,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
eq(decor.mTaskInfo.taskId),
any(),
eq(DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON),
+ anyOrNull(),
anyOrNull()
)
}
@@ -875,7 +876,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
)
verify(mockDesktopTasksController, times(1))
- .moveTaskToDesktop(any(), any(), any(), anyOrNull())
+ .moveTaskToDesktop(any(), any(), any(), anyOrNull(), anyOrNull())
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
index 2207c705d7dc..0615c1d677ba 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
@@ -51,6 +51,7 @@ import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFI
import java.util.function.Supplier
import junit.framework.Assert
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -206,6 +207,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
}
@Test
+ @Ignore("Causing presubmit failure b/391717499")
fun testDragResize_movesTask_doesNotShowResizeVeil() = runOnUiThread {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED,
@@ -245,6 +247,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
}
@Test
+ @Ignore("Causing presubmit failure b/391717499")
fun testDragResize_movesTaskToNewDisplay() = runOnUiThread {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED,
@@ -370,6 +373,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
}
@Test
+ @Ignore("Causing presubmit failure b/391717499")
fun testDragResize_drag_setBoundsNotRunIfDragEndsInDisallowedEndArea() = runOnUiThread {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED, // drag
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
index 1ec0fe794d0a..431de896f433 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
@@ -33,6 +33,7 @@ import com.android.launcher3.icons.IconProvider
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.common.UserProfileContexts
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.sysui.UserChangeListener
@@ -69,6 +70,7 @@ class WindowDecorTaskResourceLoaderTest : ShellTestCase() {
private val mockIconProvider = mock<IconProvider>()
private val mockHeaderIconFactory = mock<BaseIconFactory>()
private val mockVeilIconFactory = mock<BaseIconFactory>()
+ private val mMockUserProfileContexts = mock<UserProfileContexts>()
private lateinit var spyContext: TestableContext
private lateinit var loader: WindowDecorTaskResourceLoader
@@ -83,12 +85,13 @@ class WindowDecorTaskResourceLoaderTest : ShellTestCase() {
spyContext = spy(mContext)
spyContext.setMockPackageManager(mockPackageManager)
doReturn(spyContext).whenever(spyContext).createContextAsUser(any(), anyInt())
+ doReturn(spyContext).whenever(mMockUserProfileContexts)[anyInt()]
loader =
WindowDecorTaskResourceLoader(
- context = spyContext,
shellInit = shellInit,
shellController = mockShellController,
shellCommandHandler = mock(),
+ userProfilesContexts = mMockUserProfileContexts,
iconProvider = mockIconProvider,
headerIconFactory = mockHeaderIconFactory,
veilIconFactory = mockVeilIconFactory,
@@ -170,16 +173,6 @@ class WindowDecorTaskResourceLoaderTest : ShellTestCase() {
}
@Test
- fun testUserChange_updatesContext() {
- val newUser = 5000
- val newContext = mock<Context>()
-
- userChangeListener.onUserChanged(newUser, newContext)
-
- assertThat(loader.currentUserContext).isEqualTo(newContext)
- }
-
- @Test
fun testUserChange_clearsCache() {
val newUser = 5000
val newContext = mock<Context>()
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 677fd86aca9c..53d3b77f1ba2 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -206,6 +206,9 @@ java_sdk_library {
visibility: [
"//frameworks/base", // Framework
],
+ impl_library_visibility: [
+ "//frameworks/base/ravenwood",
+ ],
srcs: [
":framework-graphics-srcs",
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index e57148fe5a6a..3af36a404c30 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -281,7 +281,7 @@ public final class MediaRouter2 {
/* executor */ null,
/* onInstanceInvalidatedListener */ null);
} catch (IllegalArgumentException ex) {
- Log.e(TAG, "Package " + clientPackageName + " not found. Ignoring.");
+ Log.e(TAG, "Failed to create proxy router for package '" + clientPackageName + "'", ex);
return null;
}
}
diff --git a/media/java/android/media/flags/projection.aconfig b/media/java/android/media/flags/projection.aconfig
index fa1349c61c4c..6d4f0b4f47d5 100644
--- a/media/java/android/media/flags/projection.aconfig
+++ b/media/java/android/media/flags/projection.aconfig
@@ -29,3 +29,13 @@ flag {
is_exported: true
}
+flag {
+ namespace: "media_projection"
+ name: "show_stop_dialog_post_call_end"
+ description: "Shows a stop dialog for MediaProjection sessions that started during call and remain active after a call ends"
+ bug: "390343524"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ is_exported: true
+}
diff --git a/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl b/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl
index e46d34e81483..3baf4d7efd65 100644
--- a/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl
+++ b/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl
@@ -18,6 +18,7 @@ package android.media.projection;
import android.media.projection.MediaProjectionInfo;
import android.view.ContentRecordingSession;
+import android.media.projection.MediaProjectionEvent;
/** {@hide} */
oneway interface IMediaProjectionWatcherCallback {
@@ -35,4 +36,19 @@ oneway interface IMediaProjectionWatcherCallback {
in MediaProjectionInfo info,
in @nullable ContentRecordingSession session
);
+
+ /**
+ * Called when a specific {@link MediaProjectionEvent} occurs during the media projection session.
+ *
+ * @param event contains the event type, which describes the nature/context of the event.
+ * @param info optional {@link MediaProjectionInfo} containing details about the media
+ projection host.
+ * @param session the recording session for the current media projection. Can be
+ * {@code null} when the recording will stop.
+ */
+ void onMediaProjectionEvent(
+ in MediaProjectionEvent event,
+ in @nullable MediaProjectionInfo info,
+ in @nullable ContentRecordingSession session
+ );
}
diff --git a/media/java/android/media/projection/MediaProjectionEvent.aidl b/media/java/android/media/projection/MediaProjectionEvent.aidl
new file mode 100644
index 000000000000..34359900ce81
--- /dev/null
+++ b/media/java/android/media/projection/MediaProjectionEvent.aidl
@@ -0,0 +1,3 @@
+package android.media.projection;
+
+parcelable MediaProjectionEvent; \ No newline at end of file
diff --git a/media/java/android/media/projection/MediaProjectionEvent.java b/media/java/android/media/projection/MediaProjectionEvent.java
new file mode 100644
index 000000000000..6922560c8abe
--- /dev/null
+++ b/media/java/android/media/projection/MediaProjectionEvent.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.projection;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/** @hide */
+public final class MediaProjectionEvent implements Parcelable {
+
+ /**
+ * Represents various media projection events.
+ */
+ @IntDef({PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {}
+
+ /** Event type for when a call ends but the session is still active. */
+ public static final int PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL = 0;
+
+ private final @EventType int mEventType;
+ private final long mTimestampMillis;
+
+ public MediaProjectionEvent(@EventType int eventType, long timestampMillis) {
+ mEventType = eventType;
+ mTimestampMillis = timestampMillis;
+ }
+
+ private MediaProjectionEvent(Parcel in) {
+ mEventType = in.readInt();
+ mTimestampMillis = in.readLong();
+ }
+
+ public @EventType int getEventType() {
+ return mEventType;
+ }
+
+ public long getTimestampMillis() {
+ return mTimestampMillis;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof MediaProjectionEvent other) {
+ return mEventType == other.mEventType && mTimestampMillis == other.mTimestampMillis;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mEventType, mTimestampMillis);
+ }
+
+ @Override
+ public String toString() {
+ return "MediaProjectionEvent{mEventType=" + mEventType + ", mTimestampMillis="
+ + mTimestampMillis + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mEventType);
+ out.writeLong(mTimestampMillis);
+ }
+
+ public static final Parcelable.Creator<MediaProjectionEvent> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public MediaProjectionEvent createFromParcel(Parcel in) {
+ return new MediaProjectionEvent(in);
+ }
+
+ @Override
+ public MediaProjectionEvent[] newArray(int size) {
+ return new MediaProjectionEvent[size];
+ }
+ };
+}
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index 9cc2cca441a4..9036bf385d96 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -363,6 +363,19 @@ public final class MediaProjectionManager {
@Nullable ContentRecordingSession session
) {
}
+
+ /**
+ * Called when a specific {@link MediaProjectionEvent} occurs during the media projection
+ * session.
+ *
+ * @param event the media projection event details.
+ * @param info optional details about the media projection host.
+ * @param session optional associated recording session details.
+ */
+ public void onMediaProjectionEvent(
+ final MediaProjectionEvent event,
+ @Nullable MediaProjectionInfo info,
+ @Nullable final ContentRecordingSession session) {}
}
/** @hide */
@@ -405,5 +418,13 @@ public final class MediaProjectionManager {
) {
mHandler.post(() -> mCallback.onRecordingSessionSet(info, session));
}
+
+ @Override
+ public void onMediaProjectionEvent(
+ final MediaProjectionEvent event,
+ @Nullable MediaProjectionInfo info,
+ @Nullable final ContentRecordingSession session) {
+ mHandler.post(() -> mCallback.onMediaProjectionEvent(event, info, session));
+ }
}
}
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index b7269256a449..0d6d32a22dae 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -51,7 +51,6 @@ import java.util.function.Consumer;
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
@SystemService(Context.MEDIA_QUALITY_SERVICE)
public final class MediaQualityManager {
- // TODO: unhide the APIs for api review
private static final String TAG = "MediaQualityManager";
private final IMediaQualityManager mService;
@@ -123,7 +122,6 @@ public final class MediaQualityManager {
public void onPictureProfileAdded(String profileId, PictureProfile profile) {
synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
- // TODO: filter callback record
record.postPictureProfileAdded(profileId, profile);
}
}
@@ -132,7 +130,6 @@ public final class MediaQualityManager {
public void onPictureProfileUpdated(String profileId, PictureProfile profile) {
synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
- // TODO: filter callback record
record.postPictureProfileUpdated(profileId, profile);
}
}
@@ -141,7 +138,6 @@ public final class MediaQualityManager {
public void onPictureProfileRemoved(String profileId, PictureProfile profile) {
synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
- // TODO: filter callback record
record.postPictureProfileRemoved(profileId, profile);
}
}
@@ -151,7 +147,6 @@ public final class MediaQualityManager {
String profileId, List<ParameterCapability> caps) {
synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
- // TODO: filter callback record
record.postParameterCapabilitiesChanged(profileId, caps);
}
}
@@ -160,7 +155,6 @@ public final class MediaQualityManager {
public void onError(String profileId, int err) {
synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
- // TODO: filter callback record
record.postError(profileId, err);
}
}
@@ -171,7 +165,6 @@ public final class MediaQualityManager {
public void onSoundProfileAdded(String profileId, SoundProfile profile) {
synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
- // TODO: filter callback record
record.postSoundProfileAdded(profileId, profile);
}
}
@@ -180,7 +173,6 @@ public final class MediaQualityManager {
public void onSoundProfileUpdated(String profileId, SoundProfile profile) {
synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
- // TODO: filter callback record
record.postSoundProfileUpdated(profileId, profile);
}
}
@@ -189,7 +181,6 @@ public final class MediaQualityManager {
public void onSoundProfileRemoved(String profileId, SoundProfile profile) {
synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
- // TODO: filter callback record
record.postSoundProfileRemoved(profileId, profile);
}
}
@@ -199,7 +190,6 @@ public final class MediaQualityManager {
String profileId, List<ParameterCapability> caps) {
synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
- // TODO: filter callback record
record.postParameterCapabilitiesChanged(profileId, caps);
}
}
@@ -208,7 +198,6 @@ public final class MediaQualityManager {
public void onError(String profileId, int err) {
synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
- // TODO: filter callback record
record.postError(profileId, err);
}
}
diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp
index 77e2cc735895..182daeb45d7c 100644
--- a/packages/SettingsLib/BannerMessagePreference/Android.bp
+++ b/packages/SettingsLib/BannerMessagePreference/Android.bp
@@ -28,4 +28,8 @@ android_library {
sdk_version: "system_current",
min_sdk_version: "28",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.healthfitness",
+ ],
}
diff --git a/packages/SettingsLib/DisplayUtils/Android.bp b/packages/SettingsLib/DisplayUtils/Android.bp
index 279bb70d81bf..62630b5a9331 100644
--- a/packages/SettingsLib/DisplayUtils/Android.bp
+++ b/packages/SettingsLib/DisplayUtils/Android.bp
@@ -15,6 +15,4 @@ android_library {
],
srcs: ["src/**/*.java"],
-
- min_sdk_version: "21",
}
diff --git a/packages/SettingsLib/DisplayUtils/src/com/android/settingslib/display/DisplayDensityConfiguration.java b/packages/SettingsLib/DisplayUtils/src/com/android/settingslib/display/DisplayDensityConfiguration.java
index 284a9025de64..127e628fbd2f 100644
--- a/packages/SettingsLib/DisplayUtils/src/com/android/settingslib/display/DisplayDensityConfiguration.java
+++ b/packages/SettingsLib/DisplayUtils/src/com/android/settingslib/display/DisplayDensityConfiguration.java
@@ -16,13 +16,20 @@
package com.android.settingslib.display;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
import android.os.AsyncTask;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
+import android.view.Display;
+import android.view.DisplayInfo;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
+import java.util.function.Predicate;
+
/** Utility methods for controlling the display density. */
public class DisplayDensityConfiguration {
private static final String LOG_TAG = "DisplayDensityConfig";
@@ -85,4 +92,42 @@ public class DisplayDensityConfiguration {
}
});
}
+
+ /**
+ * Asynchronously applies display density changes to all displays that satisfy the predicate.
+ *
+ * <p>The change will be applied to the user specified by the value of
+ * {@link UserHandle#myUserId()} at the time the method is called.
+ *
+ * @param context The context
+ * @param predicate Determines which displays to set the density to
+ * @param density The density to force
+ */
+ public static void setForcedDisplayDensity(@NonNull Context context,
+ @NonNull Predicate<DisplayInfo> predicate, final int density) {
+ final int userId = UserHandle.myUserId();
+ DisplayManager dm = context.getSystemService(DisplayManager.class);
+ AsyncTask.execute(() -> {
+ try {
+ for (Display display : dm.getDisplays(
+ DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) {
+ int displayId = display.getDisplayId();
+ DisplayInfo info = new DisplayInfo();
+ if (!display.getDisplayInfo(info)) {
+ Log.w(LOG_TAG, "Unable to save forced display density setting "
+ + "for display " + displayId);
+ continue;
+ }
+ if (!predicate.test(info)) {
+ continue;
+ }
+
+ final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ wm.setForcedDisplayDensityForUser(displayId, density, userId);
+ }
+ } catch (RemoteException exc) {
+ Log.w(LOG_TAG, "Unable to save forced display density setting");
+ }
+ });
+ }
}
diff --git a/packages/SettingsLib/Graph/graph.proto b/packages/SettingsLib/Graph/graph.proto
index 33a7df4c6ba8..a834947144a0 100644
--- a/packages/SettingsLib/Graph/graph.proto
+++ b/packages/SettingsLib/Graph/graph.proto
@@ -26,6 +26,14 @@ message PreferenceScreenProto {
optional PreferenceGroupProto root = 2;
// If the preference screen provides complete hierarchy by source code.
optional bool complete_hierarchy = 3;
+ // Parameterized screens (not recursive, provided on the top level only)
+ repeated ParameterizedPreferenceScreenProto parameterized_screens = 4;
+}
+
+// Proto of parameterized preference screen
+message ParameterizedPreferenceScreenProto {
+ optional BundleProto args = 1;
+ optional PreferenceScreenProto screen = 2;
}
// Proto of PreferenceGroup.
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
index adffd206d552..27ce1c7246e6 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
@@ -18,12 +18,14 @@ package com.android.settingslib.graph
import android.app.Application
import android.os.Bundle
+import android.os.Parcelable
import android.os.SystemClock
import com.android.settingslib.graph.proto.PreferenceGraphProto
import com.android.settingslib.ipc.ApiHandler
import com.android.settingslib.ipc.ApiPermissionChecker
import com.android.settingslib.ipc.MessageCodec
import com.android.settingslib.metadata.PreferenceRemoteOpMetricsLogger
+import com.android.settingslib.metadata.PreferenceScreenCoordinate
import com.android.settingslib.metadata.PreferenceScreenRegistry
import com.android.settingslib.preference.PreferenceScreenProvider
import java.util.Locale
@@ -59,10 +61,9 @@ class GetPreferenceGraphApiHandler(
var success = false
try {
val builder = PreferenceGraphBuilder.of(application, callingPid, callingUid, request)
- if (request.screenKeys.isEmpty()) {
- PreferenceScreenRegistry.preferenceScreenMetadataFactories.forEachKeyAsync {
- builder.addPreferenceScreenFromRegistry(it)
- }
+ if (request.screens.isEmpty()) {
+ val factories = PreferenceScreenRegistry.preferenceScreenMetadataFactories
+ factories.forEachAsync { _, factory -> builder.addPreferenceScreen(factory) }
for (provider in preferenceScreenProviders) {
builder.addPreferenceScreenProvider(provider)
}
@@ -84,15 +85,15 @@ class GetPreferenceGraphApiHandler(
/**
* Request of [GetPreferenceGraphApiHandler].
*
- * @param screenKeys screen keys of the preference graph
- * @param visitedScreens keys of the visited preference screen
+ * @param screens screens of the preference graph
+ * @param visitedScreens visited preference screens
* @param locale locale of the preference graph
*/
data class GetPreferenceGraphRequest
@JvmOverloads
constructor(
- val screenKeys: Set<String> = setOf(),
- val visitedScreens: Set<String> = setOf(),
+ val screens: Set<PreferenceScreenCoordinate> = setOf(),
+ val visitedScreens: Set<PreferenceScreenCoordinate> = setOf(),
val locale: Locale? = null,
val flags: Int = PreferenceGetterFlags.ALL,
val includeValueDescriptor: Boolean = true,
@@ -101,26 +102,32 @@ constructor(
object GetPreferenceGraphRequestCodec : MessageCodec<GetPreferenceGraphRequest> {
override fun encode(data: GetPreferenceGraphRequest): Bundle =
Bundle(4).apply {
- putStringArray(KEY_SCREEN_KEYS, data.screenKeys.toTypedArray())
- putStringArray(KEY_VISITED_KEYS, data.visitedScreens.toTypedArray())
+ putParcelableArray(KEY_SCREENS, data.screens.toTypedArray())
+ putParcelableArray(KEY_VISITED_SCREENS, data.visitedScreens.toTypedArray())
putString(KEY_LOCALE, data.locale?.toLanguageTag())
putInt(KEY_FLAGS, data.flags)
}
+ @Suppress("DEPRECATION")
override fun decode(data: Bundle): GetPreferenceGraphRequest {
- val screenKeys = data.getStringArray(KEY_SCREEN_KEYS) ?: arrayOf()
- val visitedScreens = data.getStringArray(KEY_VISITED_KEYS) ?: arrayOf()
+ data.classLoader = PreferenceScreenCoordinate::class.java.classLoader
+ val screens = data.getParcelableArray(KEY_SCREENS) ?: arrayOf()
+ val visitedScreens = data.getParcelableArray(KEY_VISITED_SCREENS) ?: arrayOf()
fun String?.toLocale() = if (this != null) Locale.forLanguageTag(this) else null
+ fun Array<Parcelable>.toScreenCoordinates() =
+ buildSet(size) {
+ for (element in this@toScreenCoordinates) add(element as PreferenceScreenCoordinate)
+ }
return GetPreferenceGraphRequest(
- screenKeys.toSet(),
- visitedScreens.toSet(),
+ screens.toScreenCoordinates(),
+ visitedScreens.toScreenCoordinates(),
data.getString(KEY_LOCALE).toLocale(),
data.getInt(KEY_FLAGS),
)
}
- private const val KEY_SCREEN_KEYS = "k"
- private const val KEY_VISITED_KEYS = "v"
+ private const val KEY_SCREENS = "s"
+ private const val KEY_VISITED_SCREENS = "v"
private const val KEY_LOCALE = "l"
private const val KEY_FLAGS = "f"
}
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
index a9958b975fc6..1d4e2c9e1bef 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
@@ -26,6 +26,7 @@ import com.android.settingslib.ipc.ApiPermissionChecker
import com.android.settingslib.metadata.PreferenceCoordinate
import com.android.settingslib.metadata.PreferenceHierarchyNode
import com.android.settingslib.metadata.PreferenceRemoteOpMetricsLogger
+import com.android.settingslib.metadata.PreferenceScreenCoordinate
import com.android.settingslib.metadata.PreferenceScreenRegistry
/**
@@ -105,8 +106,10 @@ class PreferenceGetterApiHandler(
val errors = mutableMapOf<PreferenceCoordinate, Int>()
val preferences = mutableMapOf<PreferenceCoordinate, PreferenceProto>()
val flags = request.flags
- for ((screenKey, coordinates) in request.preferences.groupBy { it.screenKey }) {
- val screenMetadata = PreferenceScreenRegistry.create(application, screenKey)
+ val groups =
+ request.preferences.groupBy { PreferenceScreenCoordinate(it.screenKey, it.args) }
+ for ((screen, coordinates) in groups) {
+ val screenMetadata = PreferenceScreenRegistry.create(application, screen)
if (screenMetadata == null) {
val latencyMs = SystemClock.elapsedRealtime() - elapsedRealtime
for (coordinate in coordinates) {
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
index c0d244989044..4290437b0d02 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -40,6 +40,7 @@ import com.android.settingslib.graph.proto.PreferenceProto
import com.android.settingslib.graph.proto.PreferenceProto.ActionTarget
import com.android.settingslib.graph.proto.PreferenceScreenProto
import com.android.settingslib.graph.proto.TextProto
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
import com.android.settingslib.metadata.IntRangeValuePreference
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
@@ -47,7 +48,10 @@ import com.android.settingslib.metadata.PreferenceHierarchy
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceRestrictionProvider
import com.android.settingslib.metadata.PreferenceScreenBindingKeyProvider
+import com.android.settingslib.metadata.PreferenceScreenCoordinate
import com.android.settingslib.metadata.PreferenceScreenMetadata
+import com.android.settingslib.metadata.PreferenceScreenMetadataFactory
+import com.android.settingslib.metadata.PreferenceScreenMetadataParameterizedFactory
import com.android.settingslib.metadata.PreferenceScreenRegistry
import com.android.settingslib.metadata.PreferenceSummaryProvider
import com.android.settingslib.metadata.PreferenceTitleProvider
@@ -72,15 +76,19 @@ private constructor(
PreferenceScreenFactory(context.ofLocale(request.locale))
}
private val builder by lazy { PreferenceGraphProto.newBuilder() }
- private val visitedScreens = mutableSetOf<String>().apply { addAll(request.visitedScreens) }
+ private val visitedScreens = request.visitedScreens.toMutableSet()
+ private val screens = mutableMapOf<String, PreferenceScreenProto.Builder>()
private suspend fun init() {
- for (key in request.screenKeys) {
- addPreferenceScreenFromRegistry(key)
+ for (screen in request.screens) {
+ PreferenceScreenRegistry.create(context, screen)?.let { addPreferenceScreen(it) }
}
}
- fun build(): PreferenceGraphProto = builder.build()
+ fun build(): PreferenceGraphProto {
+ for ((key, screenBuilder) in screens) builder.putScreens(key, screenBuilder.build())
+ return builder.build()
+ }
/**
* Adds an activity to the graph.
@@ -138,19 +146,12 @@ private constructor(
null
}
- suspend fun addPreferenceScreenFromRegistry(key: String): Boolean {
- val metadata = PreferenceScreenRegistry.create(context, key) ?: return false
- return addPreferenceScreenMetadata(metadata)
+ private suspend fun addPreferenceScreenFromRegistry(key: String): Boolean {
+ val factory =
+ PreferenceScreenRegistry.preferenceScreenMetadataFactories[key] ?: return false
+ return addPreferenceScreen(factory)
}
- private suspend fun addPreferenceScreenMetadata(metadata: PreferenceScreenMetadata): Boolean =
- addPreferenceScreen(metadata.key) {
- preferenceScreenProto {
- completeHierarchy = metadata.hasCompleteHierarchy()
- root = metadata.getPreferenceHierarchy(context).toProto(metadata, true)
- }
- }
-
suspend fun addPreferenceScreenProvider(activityClass: Class<*>) {
Log.d(TAG, "add $activityClass")
createPreferenceScreen { activityClass.newInstance() }
@@ -188,26 +189,52 @@ private constructor(
Log.e(TAG, "\"$preferenceScreen\" has no key")
return
}
- @Suppress("CheckReturnValue") addPreferenceScreen(key) { preferenceScreen.toProto(intent) }
+ val args = preferenceScreen.peekExtras()?.getBundle(EXTRA_BINDING_SCREEN_ARGS)
+ @Suppress("CheckReturnValue")
+ addPreferenceScreen(key, args) {
+ this.intent = intent.toProto()
+ root = preferenceScreen.toProto()
+ }
+ }
+
+ suspend fun addPreferenceScreen(factory: PreferenceScreenMetadataFactory): Boolean {
+ if (factory is PreferenceScreenMetadataParameterizedFactory) {
+ factory.parameters(context).collect { addPreferenceScreen(factory.create(context, it)) }
+ return true
+ }
+ return addPreferenceScreen(factory.create(context))
}
+ private suspend fun addPreferenceScreen(metadata: PreferenceScreenMetadata): Boolean =
+ addPreferenceScreen(metadata.key, metadata.arguments) {
+ completeHierarchy = metadata.hasCompleteHierarchy()
+ root = metadata.getPreferenceHierarchy(context).toProto(metadata, true)
+ }
+
private suspend fun addPreferenceScreen(
key: String,
- preferenceScreenProvider: suspend () -> PreferenceScreenProto,
- ): Boolean =
- if (visitedScreens.add(key)) {
- builder.putScreens(key, preferenceScreenProvider())
- true
- } else {
- Log.w(TAG, "$key visited")
- false
+ args: Bundle?,
+ init: suspend PreferenceScreenProto.Builder.() -> Unit,
+ ): Boolean {
+ if (!visitedScreens.add(PreferenceScreenCoordinate(key, args))) {
+ Log.w(TAG, "$key $args visited")
+ return false
}
-
- private suspend fun PreferenceScreen.toProto(intent: Intent?): PreferenceScreenProto =
- preferenceScreenProto {
- intent?.let { this.intent = it.toProto() }
- root = (this@toProto as PreferenceGroup).toProto()
+ if (args == null) { // normal screen
+ screens[key] = PreferenceScreenProto.newBuilder().also { init(it) }
+ } else if (args.isEmpty) { // parameterized screen with backward compatibility
+ val builder = screens.getOrPut(key) { PreferenceScreenProto.newBuilder() }
+ init(builder)
+ } else { // parameterized screen with non-empty arguments
+ val builder = screens.getOrPut(key) { PreferenceScreenProto.newBuilder() }
+ val parameterizedScreen = parameterizedPreferenceScreenProto {
+ setArgs(args.toProto())
+ setScreen(PreferenceScreenProto.newBuilder().also { init(it) })
+ }
+ builder.addParameterizedScreens(parameterizedScreen)
}
+ return true
+ }
private suspend fun PreferenceGroup.toProto(): PreferenceGroupProto = preferenceGroupProto {
preference = (this@toProto as Preference).toProto()
@@ -271,7 +298,7 @@ private constructor(
.toProto(context, callingPid, callingUid, screenMetadata, isRoot, request.flags)
.also {
if (metadata is PreferenceScreenMetadata) {
- @Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata)
+ @Suppress("CheckReturnValue") addPreferenceScreen(metadata)
}
metadata.intent(context)?.resolveActivity(context.packageManager)?.let {
if (it.packageName == context.packageName) {
@@ -322,7 +349,7 @@ private constructor(
val screenKey = screen?.key
if (!screenKey.isNullOrEmpty()) {
@Suppress("CheckReturnValue")
- addPreferenceScreen(screenKey) { screen.toProto(null) }
+ addPreferenceScreen(screenKey, null) { root = screen.toProto() }
return actionTargetProto { key = screenKey }
}
} catch (e: Exception) {
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
index a595f42a573d..60f9c6bb92a3 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
@@ -40,11 +40,12 @@ import com.android.settingslib.metadata.SensitivityLevel.Companion.HIGH_SENSITIV
import com.android.settingslib.metadata.SensitivityLevel.Companion.UNKNOWN_SENSITIVITY
/** Request to set preference value. */
-data class PreferenceSetterRequest(
- val screenKey: String,
- val key: String,
+class PreferenceSetterRequest(
+ screenKey: String,
+ args: Bundle?,
+ key: String,
val value: PreferenceValueProto,
-)
+) : PreferenceCoordinate(screenKey, args, key)
/** Result of preference setter request. */
@IntDef(
@@ -121,7 +122,7 @@ class PreferenceSetterApiHandler(
metricsLogger?.logSetterApi(
application,
callingUid,
- PreferenceCoordinate(request.screenKey, request.key),
+ request,
null,
null,
PreferenceSetterResult.UNSUPPORTED,
@@ -130,7 +131,7 @@ class PreferenceSetterApiHandler(
return PreferenceSetterResult.UNSUPPORTED
}
val screenMetadata =
- PreferenceScreenRegistry.create(application, request.screenKey) ?: return notFound()
+ PreferenceScreenRegistry.create(application, request) ?: return notFound()
val key = request.key
val metadata =
screenMetadata.getPreferenceHierarchy(application).find(key) ?: return notFound()
@@ -199,7 +200,7 @@ class PreferenceSetterApiHandler(
metricsLogger?.logSetterApi(
application,
callingUid,
- PreferenceCoordinate(request.screenKey, request.key),
+ request,
screenMetadata,
metadata,
result,
@@ -235,6 +236,7 @@ object PreferenceSetterRequestCodec : MessageCodec<PreferenceSetterRequest> {
override fun encode(data: PreferenceSetterRequest) =
Bundle(3).apply {
putString(SCREEN_KEY, data.screenKey)
+ putBundle(ARGS, data.args)
putString(KEY, data.key)
putByteArray(null, data.value.toByteArray())
}
@@ -242,10 +244,12 @@ object PreferenceSetterRequestCodec : MessageCodec<PreferenceSetterRequest> {
override fun decode(data: Bundle) =
PreferenceSetterRequest(
data.getString(SCREEN_KEY)!!,
+ data.getBundle(ARGS),
data.getString(KEY)!!,
PreferenceValueProto.parseFrom(data.getByteArray(null)!!),
)
private const val SCREEN_KEY = "s"
private const val KEY = "k"
+ private const val ARGS = "a"
}
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
index adbe77318353..5f2a0d826407 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
@@ -19,6 +19,7 @@ package com.android.settingslib.graph
import com.android.settingslib.graph.proto.BundleProto
import com.android.settingslib.graph.proto.BundleProto.BundleValue
import com.android.settingslib.graph.proto.IntentProto
+import com.android.settingslib.graph.proto.ParameterizedPreferenceScreenProto
import com.android.settingslib.graph.proto.PreferenceGroupProto
import com.android.settingslib.graph.proto.PreferenceOrGroupProto
import com.android.settingslib.graph.proto.PreferenceProto
@@ -39,6 +40,12 @@ inline fun preferenceScreenProto(
init: PreferenceScreenProto.Builder.() -> Unit
): PreferenceScreenProto = PreferenceScreenProto.newBuilder().also(init).build()
+/** Kotlin DSL-style builder for [PreferenceScreenProto]. */
+inline fun parameterizedPreferenceScreenProto(
+ init: ParameterizedPreferenceScreenProto.Builder.() -> Unit
+): ParameterizedPreferenceScreenProto =
+ ParameterizedPreferenceScreenProto.newBuilder().also(init).build()
+
/** Returns preference or null. */
val PreferenceOrGroupProto.preferenceOrNull
get() = if (hasPreference()) preference else null
diff --git a/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml b/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
index 43cf6aa09109..7adcbf6c6601 100644
--- a/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
+++ b/packages/SettingsLib/IntroPreference/res/layout/settingslib_expressive_preference_intro.xml
@@ -18,6 +18,8 @@
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/entity_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
style="@style/SettingsLibEntityHeader">
<LinearLayout
diff --git a/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt b/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
index 38b641336547..69b75adea9d3 100644
--- a/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
+++ b/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
@@ -33,6 +33,9 @@ import javax.tools.Diagnostic
/** Processor to gather preference screens annotated with `@ProvidePreferenceScreen`. */
class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
private val screens = mutableListOf<Screen>()
+ private val bundleType: TypeMirror by lazy {
+ processingEnv.elementUtils.getTypeElement("android.os.Bundle").asType()
+ }
private val contextType: TypeMirror by lazy {
processingEnv.elementUtils.getTypeElement("android.content.Context").asType()
}
@@ -83,19 +86,57 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
error("@$ANNOTATION_NAME must be added to $PREFERENCE_SCREEN_METADATA subclass", this)
return
}
- val constructorType = getConstructorType()
- if (constructorType == null) {
+ fun reportConstructorError() =
error(
- "Class must be an object, or has single public constructor that " +
- "accepts no parameter or a Context parameter",
+ "Must have only one public constructor: constructor(), " +
+ "constructor(Context), constructor(Bundle) or constructor(Context, Bundle)",
this,
)
+ val constructor = findConstructor()
+ if (constructor == null || constructor.parameters.size > 2) {
+ reportConstructorError()
return
}
+ val constructorHasContextParameter = constructor.hasParameter(0, contextType)
+ var index = if (constructorHasContextParameter) 1 else 0
val annotation = annotationMirrors.single { it.isElement(annotationElement) }
val key = annotation.fieldValue<String>("value")!!
val overlay = annotation.fieldValue<Boolean>("overlay") == true
- screens.add(Screen(key, overlay, qualifiedName.toString(), constructorType))
+ val parameterized = annotation.fieldValue<Boolean>("parameterized") == true
+ var parametersHasContextParameter = false
+ if (parameterized) {
+ val parameters = findParameters()
+ if (parameters == null) {
+ error("require a static 'parameters()' or 'parameters(Context)' method", this)
+ return
+ }
+ parametersHasContextParameter = parameters
+ if (constructor.hasParameter(index, bundleType)) {
+ index++
+ } else {
+ error(
+ "Parameterized screen constructor must be" +
+ "constructor(Bundle) or constructor(Context, Bundle)",
+ this,
+ )
+ return
+ }
+ }
+ if (index == constructor.parameters.size) {
+ screens.add(
+ Screen(
+ key,
+ overlay,
+ parameterized,
+ annotation.fieldValue<Boolean>("parameterizedMigration") == true,
+ qualifiedName.toString(),
+ constructorHasContextParameter,
+ parametersHasContextParameter,
+ )
+ )
+ } else {
+ reportConstructorError()
+ }
}
private fun codegen() {
@@ -116,10 +157,15 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
screens.sort()
processingEnv.filer.createSourceFile("$outputPkg.$outputClass").openWriter().use {
it.write("package $outputPkg;\n\n")
+ it.write("import android.content.Context;\n")
+ it.write("import android.os.Bundle;\n")
it.write("import $PACKAGE.FixedArrayMap;\n")
it.write("import $PACKAGE.FixedArrayMap.OrderedInitializer;\n")
- it.write("import $PACKAGE.$FACTORY;\n\n")
- it.write("// Generated by annotation processor for @$ANNOTATION_NAME\n")
+ it.write("import $PACKAGE.$PREFERENCE_SCREEN_METADATA;\n")
+ it.write("import $PACKAGE.$FACTORY;\n")
+ it.write("import $PACKAGE.$PARAMETERIZED_FACTORY;\n")
+ it.write("import kotlinx.coroutines.flow.Flow;\n")
+ it.write("\n// Generated by annotation processor for @$ANNOTATION_NAME\n")
it.write("public final class $outputClass {\n")
it.write(" private $outputClass() {}\n\n")
it.write(" public static FixedArrayMap<String, $FACTORY> $outputFun() {\n")
@@ -127,10 +173,29 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
it.write(" return new FixedArrayMap<>($size, $outputClass::init);\n")
it.write(" }\n\n")
fun Screen.write() {
- it.write(" screens.put(\"$key\", context -> new $klass(")
- when (constructorType) {
- ConstructorType.DEFAULT -> it.write("));")
- ConstructorType.CONTEXT -> it.write("context));")
+ it.write(" screens.put(\"$key\", ")
+ if (parameterized) {
+ it.write("new $PARAMETERIZED_FACTORY() {\n")
+ it.write(" @Override public PreferenceScreenMetadata create")
+ it.write("(Context context, Bundle args) {\n")
+ it.write(" return new $klass(")
+ if (constructorHasContextParameter) it.write("context, ")
+ it.write("args);\n")
+ it.write(" }\n\n")
+ it.write(" @Override public Flow<Bundle> parameters(Context context) {\n")
+ it.write(" return $klass.parameters(")
+ if (parametersHasContextParameter) it.write("context")
+ it.write(");\n")
+ it.write(" }\n")
+ if (parameterizedMigration) {
+ it.write("\n @Override public boolean acceptEmptyArguments()")
+ it.write(" { return true; }\n")
+ }
+ it.write(" });")
+ } else {
+ it.write("context -> new $klass(")
+ if (constructorHasContextParameter) it.write("context")
+ it.write("));")
}
if (overlay) it.write(" // overlay")
it.write("\n")
@@ -159,7 +224,7 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
}
private fun AnnotationMirror.isElement(element: TypeElement) =
- processingEnv.typeUtils.isSameType(annotationType.asElement().asType(), element.asType())
+ annotationType.asElement().asType().isSameType(element.asType())
@Suppress("UNCHECKED_CAST")
private fun <T> AnnotationMirror.fieldValue(name: String): T? = field(name)?.value as? T
@@ -171,7 +236,7 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
return null
}
- private fun TypeElement.getConstructorType(): ConstructorType? {
+ private fun TypeElement.findConstructor(): ExecutableElement? {
var constructor: ExecutableElement? = null
for (element in enclosedElements) {
if (element.kind != ElementKind.CONSTRUCTOR) continue
@@ -179,16 +244,30 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
if (constructor != null) return null
constructor = element as ExecutableElement
}
- return constructor?.parameters?.run {
- when {
- isEmpty() -> ConstructorType.DEFAULT
- size == 1 && processingEnv.typeUtils.isSameType(this[0].asType(), contextType) ->
- ConstructorType.CONTEXT
- else -> null
- }
+ return constructor
+ }
+
+ private fun TypeElement.findParameters(): Boolean? {
+ for (element in enclosedElements) {
+ if (element.kind != ElementKind.METHOD) continue
+ if (!element.modifiers.contains(Modifier.PUBLIC)) continue
+ if (!element.modifiers.contains(Modifier.STATIC)) continue
+ if (!element.simpleName.contentEquals("parameters")) return null
+ val parameters = (element as ExecutableElement).parameters
+ if (parameters.isEmpty()) return false
+ if (parameters.size == 1 && parameters[0].asType().isSameType(contextType)) return true
+ error("parameters method should have no parameter or a Context parameter", element)
+ return null
}
+ return null
}
+ private fun ExecutableElement.hasParameter(index: Int, typeMirror: TypeMirror) =
+ index < parameters.size && parameters[index].asType().isSameType(typeMirror)
+
+ private fun TypeMirror.isSameType(typeMirror: TypeMirror) =
+ processingEnv.typeUtils.isSameType(this, typeMirror)
+
private fun warn(msg: CharSequence) =
processingEnv.messager.printMessage(Diagnostic.Kind.WARNING, msg)
@@ -198,8 +277,11 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
private data class Screen(
val key: String,
val overlay: Boolean,
+ val parameterized: Boolean,
+ val parameterizedMigration: Boolean,
val klass: String,
- val constructorType: ConstructorType,
+ val constructorHasContextParameter: Boolean,
+ val parametersHasContextParameter: Boolean,
) : Comparable<Screen> {
override fun compareTo(other: Screen): Int {
val diff = key.compareTo(other.key)
@@ -207,17 +289,13 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
}
}
- private enum class ConstructorType {
- DEFAULT, // default constructor with no parameter
- CONTEXT, // constructor with a Context parameter
- }
-
companion object {
private const val PACKAGE = "com.android.settingslib.metadata"
private const val ANNOTATION_NAME = "ProvidePreferenceScreen"
private const val ANNOTATION = "$PACKAGE.$ANNOTATION_NAME"
private const val PREFERENCE_SCREEN_METADATA = "PreferenceScreenMetadata"
private const val FACTORY = "PreferenceScreenMetadataFactory"
+ private const val PARAMETERIZED_FACTORY = "PreferenceScreenMetadataParameterizedFactory"
private const val OPTIONS_NAME = "ProvidePreferenceScreenOptions"
private const val OPTIONS = "$PACKAGE.$OPTIONS_NAME"
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
index 4bed795ea760..449c78ce8965 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
@@ -22,14 +22,27 @@ package com.android.settingslib.metadata
* The annotated class must satisfy either condition:
* - the primary constructor has no parameter
* - the primary constructor has a single [android.content.Context] parameter
+ * - (parameterized) the primary constructor has a single [android.os.Bundle] parameter to override
+ * [PreferenceScreenMetadata.arguments]
+ * - (parameterized) the primary constructor has a [android.content.Context] and a
+ * [android.os.Bundle] parameter to override [PreferenceScreenMetadata.arguments]
*
* @param value unique preference screen key
* @param overlay if true, current annotated screen will overlay the screen that has identical key
+ * @param parameterized if true, the screen relies on additional arguments to build its content
+ * @param parameterizedMigration whether the parameterized screen was a normal screen, in which case
+ * `Bundle.EMPTY` will be passed as arguments to take care of backward compatibility
+ * @see PreferenceScreenMetadata
*/
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.CLASS)
@MustBeDocumented
-annotation class ProvidePreferenceScreen(val value: String, val overlay: Boolean = false)
+annotation class ProvidePreferenceScreen(
+ val value: String,
+ val overlay: Boolean = false,
+ val parameterized: Boolean = false,
+ val parameterizedMigration: Boolean = false, // effective only when parameterized is true
+)
/**
* Provides options for [ProvidePreferenceScreen] annotation processor.
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Bundles.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Bundles.kt
new file mode 100644
index 000000000000..a63576510aec
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Bundles.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.metadata
+
+import android.content.Intent
+import android.os.Bundle
+
+@Suppress("DEPRECATION")
+fun Bundle?.contentEquals(other: Bundle?): Boolean {
+ if (this == null) return other == null
+ if (other == null) return false
+ if (keySet() != other.keySet()) return false
+ fun Any?.valueEquals(other: Any?) =
+ when (this) {
+ is Bundle -> other is Bundle && this.contentEquals(other)
+ is Intent -> other is Intent && this.filterEquals(other)
+ is BooleanArray -> other is BooleanArray && this contentEquals other
+ is ByteArray -> other is ByteArray && this contentEquals other
+ is CharArray -> other is CharArray && this contentEquals other
+ is DoubleArray -> other is DoubleArray && this contentEquals other
+ is FloatArray -> other is FloatArray && this contentEquals other
+ is IntArray -> other is IntArray && this contentEquals other
+ is LongArray -> other is LongArray && this contentEquals other
+ is ShortArray -> other is ShortArray && this contentEquals other
+ is Array<*> -> other is Array<*> && this contentDeepEquals other
+ else -> this == other
+ }
+ for (key in keySet()) {
+ if (!get(key).valueEquals(other.get(key))) return false
+ }
+ return true
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
index 63f1050df94e..e456a7f1aa1c 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
@@ -78,13 +78,8 @@ annotation class SensitivityLevel {
/** Preference metadata that has a value persisted in datastore. */
interface PersistentPreference<T> : PreferenceMetadata {
- /**
- * The value type the preference is associated with.
- *
- * TODO(b/388167302): Remove the default implementation once all subclasses are migrated.
- */
- val valueType: Class<T>?
- get() = null
+ /** The value type the preference is associated with. */
+ val valueType: Class<T>
/**
* Returns the key-value storage of the preference.
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceCoordinate.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceCoordinate.kt
index 2dd736ae6083..ac08847b6002 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceCoordinate.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceCoordinate.kt
@@ -16,26 +16,41 @@
package com.android.settingslib.metadata
+import android.os.Bundle
import android.os.Parcel
import android.os.Parcelable
/**
* Coordinate to locate a preference.
*
- * Within an app, the preference screen key (unique among screens) plus preference key (unique on
- * the screen) is used to locate a preference.
+ * Within an app, the preference screen coordinate (unique among screens) plus preference key
+ * (unique on the screen) is used to locate a preference.
*/
-data class PreferenceCoordinate(val screenKey: String, val key: String) : Parcelable {
+open class PreferenceCoordinate : PreferenceScreenCoordinate {
+ val key: String
- constructor(parcel: Parcel) : this(parcel.readString()!!, parcel.readString()!!)
+ constructor(screenKey: String, key: String) : this(screenKey, null, key)
+
+ constructor(screenKey: String, args: Bundle?, key: String) : super(screenKey, args) {
+ this.key = key
+ }
+
+ constructor(parcel: Parcel) : super(parcel) {
+ this.key = parcel.readString()!!
+ }
override fun writeToParcel(parcel: Parcel, flags: Int) {
- parcel.writeString(screenKey)
+ super.writeToParcel(parcel, flags)
parcel.writeString(key)
}
override fun describeContents() = 0
+ override fun equals(other: Any?) =
+ super.equals(other) && key == (other as PreferenceCoordinate).key
+
+ override fun hashCode() = super.hashCode() xor key.hashCode()
+
companion object CREATOR : Parcelable.Creator<PreferenceCoordinate> {
override fun createFromParcel(parcel: Parcel) = PreferenceCoordinate(parcel)
@@ -43,3 +58,46 @@ data class PreferenceCoordinate(val screenKey: String, val key: String) : Parcel
override fun newArray(size: Int) = arrayOfNulls<PreferenceCoordinate>(size)
}
}
+
+/** Coordinate to locate a preference screen. */
+open class PreferenceScreenCoordinate : Parcelable {
+ /** Unique preference screen key. */
+ val screenKey: String
+
+ /** Arguments to create parameterized preference screen. */
+ val args: Bundle?
+
+ constructor(screenKey: String, args: Bundle?) {
+ this.screenKey = screenKey
+ this.args = args
+ }
+
+ constructor(parcel: Parcel) {
+ screenKey = parcel.readString()!!
+ args = parcel.readBundle(javaClass.classLoader)
+ }
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeString(screenKey)
+ parcel.writeBundle(args)
+ }
+
+ override fun describeContents() = 0
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+ other as PreferenceScreenCoordinate
+ return screenKey == other.screenKey && args.contentEquals(other.args)
+ }
+
+ // "args" is not included intentionally, otherwise we need to take care of array, etc.
+ override fun hashCode() = screenKey.hashCode()
+
+ companion object CREATOR : Parcelable.Creator<PreferenceScreenCoordinate> {
+
+ override fun createFromParcel(parcel: Parcel) = PreferenceScreenCoordinate(parcel)
+
+ override fun newArray(size: Int) = arrayOfNulls<PreferenceScreenCoordinate>(size)
+ }
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
index 876f6152cccd..3bd051dee41d 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.metadata
import android.content.Context
+import android.os.Bundle
/** A node in preference hierarchy that is associated with [PreferenceMetadata]. */
open class PreferenceHierarchyNode internal constructor(val metadata: PreferenceMetadata) {
@@ -54,8 +55,14 @@ internal constructor(private val context: Context, metadata: PreferenceMetadata)
*
* @throws NullPointerException if screen is not registered to [PreferenceScreenRegistry]
*/
- operator fun String.unaryPlus() =
- +PreferenceHierarchyNode(PreferenceScreenRegistry.create(context, this)!!)
+ operator fun String.unaryPlus() = addPreferenceScreen(this, null)
+
+ /**
+ * Adds parameterized preference screen with given key (as a placeholder) to the hierarchy.
+ *
+ * @see String.unaryPlus
+ */
+ infix fun String.args(args: Bundle) = createPreferenceScreenHierarchy(this, args)
operator fun PreferenceHierarchyNode.unaryPlus() = also { children.add(it) }
@@ -122,6 +129,14 @@ internal constructor(private val context: Context, metadata: PreferenceMetadata)
}
/**
+ * Adds parameterized preference screen with given key (as a placeholder) to the hierarchy.
+ *
+ * @see addPreferenceScreen
+ */
+ fun addParameterizedScreen(screenKey: String, args: Bundle) =
+ addPreferenceScreen(screenKey, args)
+
+ /**
* Adds preference screen with given key (as a placeholder) to the hierarchy.
*
* This is mainly to support Android Settings overlays. OEMs might want to custom some of the
@@ -132,11 +147,13 @@ internal constructor(private val context: Context, metadata: PreferenceMetadata)
*
* @throws NullPointerException if screen is not registered to [PreferenceScreenRegistry]
*/
- fun addPreferenceScreen(screenKey: String) {
- children.add(
- PreferenceHierarchy(context, PreferenceScreenRegistry.create(context, screenKey)!!)
- )
- }
+ fun addPreferenceScreen(screenKey: String) = addPreferenceScreen(screenKey, null)
+
+ private fun addPreferenceScreen(screenKey: String, args: Bundle?): PreferenceHierarchyNode =
+ createPreferenceScreenHierarchy(screenKey, args).also { children.add(it) }
+
+ private fun createPreferenceScreenHierarchy(screenKey: String, args: Bundle?) =
+ PreferenceHierarchyNode(PreferenceScreenRegistry.create(context, screenKey, args)!!)
/** Extensions to add more preferences to the hierarchy. */
operator fun PreferenceHierarchy.plusAssign(init: PreferenceHierarchy.() -> Unit) = init(this)
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt
index 84014f191f68..4fd13ede6803 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt
@@ -17,13 +17,20 @@
package com.android.settingslib.metadata
import android.content.Context
+import android.os.Bundle
/** Provides the associated preference screen key for binding. */
interface PreferenceScreenBindingKeyProvider {
/** Returns the associated preference screen key. */
fun getPreferenceScreenBindingKey(context: Context): String?
+
+ /** Returns the arguments to build preference screen. */
+ fun getPreferenceScreenBindingArgs(context: Context): Bundle?
}
/** Extra key to provide the preference screen key for binding. */
const val EXTRA_BINDING_SCREEN_KEY = "settingslib:binding_screen_key"
+
+/** Extra key to provide arguments for preference screen binding. */
+const val EXTRA_BINDING_SCREEN_ARGS = "settingslib:binding_screen_args"
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt
index 850d4523e96e..7f1ded71e30a 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt
@@ -18,12 +18,25 @@ package com.android.settingslib.metadata
import android.content.Context
import android.content.Intent
+import android.os.Bundle
import androidx.annotation.AnyThread
import androidx.fragment.app.Fragment
+import kotlinx.coroutines.flow.Flow
-/** Metadata of preference screen. */
+/**
+ * Metadata of preference screen.
+ *
+ * For parameterized preference screen that relies on additional information (e.g. package name,
+ * language code) to build its content, the subclass must:
+ * - override [arguments] in constructor
+ * - add a static method `fun parameters(context: Context): List<Bundle>` (context is optional) to
+ * provide all possible arguments
+ */
@AnyThread
interface PreferenceScreenMetadata : PreferenceMetadata {
+ /** Arguments to build the screen content. */
+ val arguments: Bundle?
+ get() = null
/**
* The screen title resource, which precedes [getScreenTitle] if provided.
@@ -65,7 +78,12 @@ interface PreferenceScreenMetadata : PreferenceMetadata {
fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?): Intent? = null
}
-/** Factory of [PreferenceScreenMetadata]. */
+/**
+ * Factory of [PreferenceScreenMetadata].
+ *
+ * Annotation processor generates implementation of this interface based on
+ * [ProvidePreferenceScreen] when [ProvidePreferenceScreen.parameterized] is `false`.
+ */
fun interface PreferenceScreenMetadataFactory {
/**
@@ -75,3 +93,44 @@ fun interface PreferenceScreenMetadataFactory {
*/
fun create(context: Context): PreferenceScreenMetadata
}
+
+/**
+ * Parameterized factory of [PreferenceScreenMetadata].
+ *
+ * Annotation processor generates implementation of this interface based on
+ * [ProvidePreferenceScreen] when [ProvidePreferenceScreen.parameterized] is `true`.
+ */
+interface PreferenceScreenMetadataParameterizedFactory : PreferenceScreenMetadataFactory {
+ override fun create(context: Context) = create(context, Bundle.EMPTY)
+
+ /**
+ * Creates a new [PreferenceScreenMetadata] with given arguments.
+ *
+ * @param context application context to create the PreferenceScreenMetadata
+ * @param args arguments to create the screen metadata, [Bundle.EMPTY] is reserved for the
+ * default case when screen is migrated from normal to parameterized
+ */
+ fun create(context: Context, args: Bundle): PreferenceScreenMetadata
+
+ /**
+ * Returns all possible arguments to create [PreferenceScreenMetadata].
+ *
+ * Note that [Bundle.EMPTY] is a special arguments reserved for backward compatibility when a
+ * preference screen was a normal screen but migrated to parameterized screen later:
+ * 1. Set [ProvidePreferenceScreen.parameterizedMigration] to `true`, so that the generated
+ * [acceptEmptyArguments] will be `true`.
+ * 1. In the original [parameters] implementation, produce a [Bundle.EMPTY] for the default
+ * case.
+ *
+ * Do not use [Bundle.EMPTY] for other purpose.
+ */
+ fun parameters(context: Context): Flow<Bundle>
+
+ /**
+ * Returns true when the parameterized screen was a normal screen.
+ *
+ * The [PreferenceScreenMetadata] is expected to accept an empty arguments ([Bundle.EMPTY]) and
+ * take care of backward compatibility.
+ */
+ fun acceptEmptyArguments(): Boolean = false
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
index c74b3151abb2..246310984db9 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
@@ -17,10 +17,13 @@
package com.android.settingslib.metadata
import android.content.Context
+import android.os.Bundle
+import android.util.Log
import com.android.settingslib.datastore.KeyValueStore
/** Registry of all available preference screens in the app. */
object PreferenceScreenRegistry : ReadWritePermitProvider {
+ private const val TAG = "ScreenRegistry"
/** Provider of key-value store. */
private lateinit var keyValueStoreProvider: KeyValueStoreProvider
@@ -52,9 +55,28 @@ object PreferenceScreenRegistry : ReadWritePermitProvider {
fun getKeyValueStore(context: Context, preference: PreferenceMetadata): KeyValueStore? =
keyValueStoreProvider.getKeyValueStore(context, preference)
- /** Creates [PreferenceScreenMetadata] of particular screen key. */
- fun create(context: Context, screenKey: String?): PreferenceScreenMetadata? =
- screenKey?.let { preferenceScreenMetadataFactories[it]?.create(context.applicationContext) }
+ /** Creates [PreferenceScreenMetadata] of particular screen. */
+ fun create(context: Context, screenCoordinate: PreferenceScreenCoordinate) =
+ create(context, screenCoordinate.screenKey, screenCoordinate.args)
+
+ /** Creates [PreferenceScreenMetadata] of particular screen key with given arguments. */
+ fun create(context: Context, screenKey: String?, args: Bundle?): PreferenceScreenMetadata? {
+ if (screenKey == null) return null
+ val factory = preferenceScreenMetadataFactories[screenKey] ?: return null
+ val appContext = context.applicationContext
+ if (factory is PreferenceScreenMetadataParameterizedFactory) {
+ if (args != null) return factory.create(appContext, args)
+ // In case the parameterized screen was a normal scree, it is expected to accept
+ // Bundle.EMPTY arguments and take care of backward compatibility.
+ if (factory.acceptEmptyArguments()) return factory.create(appContext)
+ Log.e(TAG, "screen $screenKey is parameterized but args is not provided")
+ return null
+ } else {
+ if (args == null) return factory.create(appContext)
+ Log.e(TAG, "screen $screenKey is not parameterized but args is provided")
+ return null
+ }
+ }
/**
* Sets the provider to check read write permit. Read and write requests are denied by default.
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
index 65fbe2b66e77..dbac17d4e8b8 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
@@ -22,6 +22,7 @@ import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import androidx.preference.TwoStatePreference
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceScreenMetadata
@@ -35,9 +36,11 @@ interface PreferenceScreenBinding : PreferenceBinding {
super.bind(preference, metadata)
val context = preference.context
val screenMetadata = metadata as PreferenceScreenMetadata
+ val extras = preference.extras
// Pass the preference key to fragment, so that the fragment could find associated
// preference screen registered in PreferenceScreenRegistry
- preference.extras.putString(EXTRA_BINDING_SCREEN_KEY, preference.key)
+ extras.putString(EXTRA_BINDING_SCREEN_KEY, preference.key)
+ screenMetadata.arguments?.let { extras.putBundle(EXTRA_BINDING_SCREEN_ARGS, it) }
if (preference is PreferenceScreen) {
val screenTitle = screenMetadata.screenTitle
preference.title =
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
index ffe181d0c350..02f91c1bb50b 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
@@ -23,6 +23,7 @@ import android.util.Log
import androidx.annotation.XmlRes
import androidx.lifecycle.Lifecycle
import androidx.preference.PreferenceScreen
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
import com.android.settingslib.metadata.PreferenceScreenBindingKeyProvider
import com.android.settingslib.metadata.PreferenceScreenRegistry
@@ -89,13 +90,19 @@ open class PreferenceFragment :
@XmlRes protected open fun getPreferenceScreenResId(context: Context): Int = 0
protected fun getPreferenceScreenCreator(context: Context): PreferenceScreenCreator? =
- (PreferenceScreenRegistry.create(context, getPreferenceScreenBindingKey(context))
- as? PreferenceScreenCreator)
+ (PreferenceScreenRegistry.create(
+ context,
+ getPreferenceScreenBindingKey(context),
+ getPreferenceScreenBindingArgs(context),
+ ) as? PreferenceScreenCreator)
?.run { if (isFlagEnabled(context)) this else null }
override fun getPreferenceScreenBindingKey(context: Context): String? =
arguments?.getString(EXTRA_BINDING_SCREEN_KEY)
+ override fun getPreferenceScreenBindingArgs(context: Context): Bundle? =
+ arguments?.getBundle(EXTRA_BINDING_SCREEN_ARGS)
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
preferenceScreenBindingHelper?.onCreate()
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index 4a6a589cd3c9..1cb8005ddae0 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -31,6 +31,7 @@ import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.KeyedDataObservable
import com.android.settingslib.datastore.KeyedObservable
import com.android.settingslib.datastore.KeyedObserver
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceChangeReason
import com.android.settingslib.metadata.PreferenceHierarchy
@@ -227,14 +228,16 @@ class PreferenceScreenBindingHelper(
/** Updates preference screen that has incomplete hierarchy. */
@JvmStatic
fun bind(preferenceScreen: PreferenceScreen) {
- PreferenceScreenRegistry.create(preferenceScreen.context, preferenceScreen.key)?.run {
+ val context = preferenceScreen.context
+ val args = preferenceScreen.peekExtras()?.getBundle(EXTRA_BINDING_SCREEN_ARGS)
+ PreferenceScreenRegistry.create(context, preferenceScreen.key, args)?.run {
if (!hasCompleteHierarchy()) {
val preferenceBindingFactory =
(this as? PreferenceScreenCreator)?.preferenceBindingFactory ?: return
bindRecursively(
preferenceScreen,
preferenceBindingFactory,
- getPreferenceHierarchy(preferenceScreen.context),
+ getPreferenceHierarchy(context),
)
}
}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
index 211b3bdaea70..88c4fe6bf188 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
@@ -17,10 +17,12 @@
package com.android.settingslib.preference
import android.content.Context
+import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import androidx.preference.PreferenceScreen
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
import com.android.settingslib.metadata.PreferenceScreenRegistry
/** Factory to create preference screen. */
@@ -81,8 +83,12 @@ class PreferenceScreenFactory {
*
* The screen must be registered in [PreferenceScreenFactory] and provide a complete hierarchy.
*/
- fun createBindingScreen(context: Context, screenKey: String?): PreferenceScreen? {
- val metadata = PreferenceScreenRegistry.create(context, screenKey) ?: return null
+ fun createBindingScreen(
+ context: Context,
+ screenKey: String?,
+ args: Bundle?,
+ ): PreferenceScreen? {
+ val metadata = PreferenceScreenRegistry.create(context, screenKey, args) ?: return null
if (metadata is PreferenceScreenCreator && metadata.hasCompleteHierarchy()) {
return metadata.createPreferenceScreen(this)
}
@@ -94,8 +100,9 @@ class PreferenceScreenFactory {
@JvmStatic
fun createBindingScreen(preference: Preference): PreferenceScreen? {
val context = preference.context
+ val args = preference.peekExtras()?.getBundle(EXTRA_BINDING_SCREEN_ARGS)
val preferenceScreenCreator =
- (PreferenceScreenRegistry.create(context, preference.key)
+ (PreferenceScreenRegistry.create(context, preference.key, args)
as? PreferenceScreenCreator) ?: return null
if (!preferenceScreenCreator.hasCompleteHierarchy()) return null
val factory = PreferenceScreenFactory(context)
diff --git a/packages/SettingsLib/SettingsTheme/res/values-af/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-af/strings.xml
index 92bf10164c24..5ebeab57b42b 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-af/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-af/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Vou uit"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Vou in"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Maak toe"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-am/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-am/strings.xml
index 24811e4ca570..27dda46b72cf 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-am/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-am/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ዘርጋ"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"ሰብስብ"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"አሰናብት"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-ar/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ar/strings.xml
index 87a7f29f4ba8..522977e596bb 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-ar/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-ar/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"توسيع"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"تصغير"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"إغلاق"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-az/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-az/strings.xml
index 6bbc371f7643..4ba23ddb1382 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-az/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-az/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Genişləndirin"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Yığcamlaşdırın"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Qapadın"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-be/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-be/strings.xml
index df590aba9b84..e92bae7fb49b 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-be/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-be/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Разгарнуць"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Згарнуць"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Закрыць"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-bg/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-bg/strings.xml
index fde9b66a93e0..4a7f6a526e49 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-bg/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-bg/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Разгъване"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Свиване"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Отхвърляне"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-bn/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-bn/strings.xml
index 78e081ff9a3f..22be594c76ae 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-bn/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-bn/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"বড় করুন"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"আড়াল করুন"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"বাতিল করুন"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-bs/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-bs/strings.xml
index a0af83bd7685..3498577f3a7f 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-bs/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-bs/strings.xml
@@ -19,5 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Proširi"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Suzi"</string>
- <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Odbaci"</string>
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Odbacivanje"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-ca/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ca/strings.xml
index c656456df0f7..0279fdea78cb 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-ca/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-ca/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Desplega"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Replega"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Ignora"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-cs/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-cs/strings.xml
index 0c1e5a7c23d2..82e88c476f80 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-cs/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-cs/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Rozbalit"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Sbalit"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Zavřít"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-da/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-da/strings.xml
index 50d06c3cb613..fc72e00434b1 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-da/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-da/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Udvid"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Skjul"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Luk"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-de/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-de/strings.xml
index 13cee4b71e60..cdf6da04173c 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-de/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-de/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Maximieren"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Minimieren"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Schließen"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-el/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-el/strings.xml
index 9b3f66f79540..6f6b753714d4 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-el/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-el/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Ανάπτυξη"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Σύμπτυξη"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Παράβλεψη"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-en-rAU/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-en-rAU/strings.xml
index 5dd479898b81..bb98403afa91 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-en-rAU/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Expand"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Collapse"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Dismiss"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-en-rGB/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-en-rGB/strings.xml
index 5dd479898b81..bb98403afa91 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-en-rGB/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Expand"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Collapse"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Dismiss"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-en-rIN/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-en-rIN/strings.xml
index 5dd479898b81..bb98403afa91 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-en-rIN/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Expand"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Collapse"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Dismiss"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-es-rUS/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-es-rUS/strings.xml
index 39906176c553..8e70d7c6ed60 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-es-rUS/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Expandir"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Contraer"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Descartar"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-es/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-es/strings.xml
index 5e5c6ab35d9a..8fc34e254d47 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-es/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-es/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Mostrar"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Ocultar"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Cerrar"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-et/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-et/strings.xml
index 9b32826ca658..92ca8492fe6c 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-et/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-et/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Laienda"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Ahenda"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Loobumine"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-eu/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-eu/strings.xml
index 5fca617bd412..e352421fdc45 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-eu/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-eu/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Zabaldu"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Tolestu"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Baztertu"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-fa/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-fa/strings.xml
index ed8f42a6fe2d..5b71864cbc7e 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-fa/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-fa/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ازهم بازکردن"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"جمع کردن"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"بستن"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-fi/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-fi/strings.xml
index da7b321df38d..b4f46b5b53b9 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-fi/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-fi/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Laajenna"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Tiivistä"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Ohita"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-fr-rCA/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-fr-rCA/strings.xml
index 443131aab0c1..d5495df76ced 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-fr-rCA/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Développer"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Réduire"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Fermer"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-fr/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-fr/strings.xml
index 443131aab0c1..d5495df76ced 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-fr/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-fr/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Développer"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Réduire"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Fermer"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-gl/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-gl/strings.xml
index 1da2701c40dd..1f7bb4285b80 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-gl/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-gl/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Despregar"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Contraer"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Pechar"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-gu/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-gu/strings.xml
index 2bf8eea96684..90a6c4e0fa4f 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-gu/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-gu/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"મોટું કરો"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"નાનું કરો"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"છોડી દો"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-hi/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-hi/strings.xml
index 8dec2a0164a0..e24aa793da80 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-hi/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-hi/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"बड़ा करें"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"छोटा करें"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"खारिज करें"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-hu/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-hu/strings.xml
index f1cf20091658..88e54d248fd5 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-hu/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-hu/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Kibontás"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Összecsukás"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Elvetés"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-hy/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-hy/strings.xml
index 7e184c665de8..b7b09b3f6b4d 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-hy/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-hy/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Ծավալել"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Ծալել"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Փակել"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-in/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-in/strings.xml
index 4bce40172d27..bfe166c5e9d7 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-in/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-in/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Luaskan"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Ciutkan"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Tutup"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-is/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-is/strings.xml
index d902452d03bc..d905546e7d36 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-is/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-is/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Stækka"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Minnka"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Loka"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-it/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-it/strings.xml
index 1dad608f8f76..88a7a60d73a9 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-it/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-it/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Espandi"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Comprimi"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Ignora"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-iw/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-iw/strings.xml
index 154695f2c796..80e48aca8497 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-iw/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-iw/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"הרחבה"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"כיווץ"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"סגירה"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-ja/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ja/strings.xml
index c1c53e0aae04..8cd92737db31 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-ja/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-ja/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"開く"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"閉じる"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"閉じる"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-kk/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-kk/strings.xml
index 11e3389e934c..801e938ceea7 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-kk/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-kk/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Жаю"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Жию"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Жабу"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-km/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-km/strings.xml
index 97f29c4c7dcc..36d81e6213b2 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-km/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-km/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ពង្រីក"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"បង្រួម"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"ច្រានចោល"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-kn/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-kn/strings.xml
index 19688058c640..c11b77cb2ef1 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-kn/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-kn/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ವಿಸ್ತೃತಗೊಳಿಸಿ"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"ಕುಗ್ಗಿಸಿ"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"ವಜಾಗೊಳಿಸಿ"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-ko/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ko/strings.xml
index db6f20872a1d..0724d5b99d32 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-ko/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-ko/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"펼치기"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"접기"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"닫기"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-ky/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ky/strings.xml
index 2a48e8e41b3a..b2c16e940e15 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-ky/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-ky/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Жайып көрсөтүү"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Жыйыштыруу"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Жабуу"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-lv/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-lv/strings.xml
index 016620d73c25..aa2bd8c64efb 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-lv/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-lv/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Izvērst"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Sakļaut"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Nerādīt"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-mk/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-mk/strings.xml
index 459b4daeb2cf..704deb6d197f 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-mk/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-mk/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Прошири"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Собери"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Отфрли"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-ml/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ml/strings.xml
index 2ce7c427c831..e83d44db6994 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-ml/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-ml/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"വികസിപ്പിക്കുക"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"ചുരുക്കുക"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"ഡിസ്‌മിസ് ചെയ്യുക"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-mn/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-mn/strings.xml
index e895d4d884cd..dda19c3bc510 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-mn/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-mn/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Дэлгэх"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Хураах"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Хаах"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-my/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-my/strings.xml
index fa854f67b8ba..22aa721edb92 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-my/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-my/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ပိုပြပါ"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"လျှော့ပြပါ"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"ပယ်ရန်"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-nb/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-nb/strings.xml
index 1a5652b3bdc3..c08ea5492888 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-nb/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-nb/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Vis"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Skjul"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Lukk"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-ne/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ne/strings.xml
index d9badf853557..b2ccbc5ef90b 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-ne/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-ne/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"एक्स्पान्ड गर्नुहोस्"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"कोल्याप्स गर्नुहोस्"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"बन्द गर्नुहोस्"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-nl/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-nl/strings.xml
index 6a904028cf24..7469d3f34ee4 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-nl/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-nl/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Uitvouwen"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Samenvouwen"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Sluiten"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-or/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-or/strings.xml
index f8c665d22b65..18701e87034f 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-or/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-or/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ବିସ୍ତାର କରନ୍ତୁ"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"ଖାରଜ କରନ୍ତୁ"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-pa/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-pa/strings.xml
index fd641d9e3db9..a47dec4030d2 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-pa/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-pa/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"ਸਮੇਟੋ"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"ਖਾਰਜ ਕਰੋ"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-ro/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ro/strings.xml
index 1471a64708a1..2b705cfd4708 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-ro/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-ro/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Extinde"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Restrânge"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Închide"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-ru/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ru/strings.xml
index 324dd7405d6f..ec17fb956862 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-ru/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-ru/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Развернуть"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Свернуть"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Закрыть"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-sl/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-sl/strings.xml
index aeb2743c9e9e..4b2483776edf 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-sl/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-sl/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Razširi"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Strni"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Opusti"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-sq/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-sq/strings.xml
index 05205a89188b..8accb18c9aa0 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-sq/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-sq/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Zgjero"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Palos"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Hiq"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-sv/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-sv/strings.xml
index 0d0539864ea5..67d398406360 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-sv/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-sv/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Utöka"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Komprimera"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Stäng"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-sw/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-sw/strings.xml
index 03f5565cc170..81cb868fc43b 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-sw/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-sw/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Panua"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Kunja"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Ondoa"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-ta/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ta/strings.xml
index 790efbf31774..27bae1f2846c 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-ta/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-ta/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"விரிவாக்கும்"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"சுருக்கும்"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"மூடும்"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-te/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-te/strings.xml
index 6a0c6603757c..55a1c3ab924c 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-te/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-te/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"విస్తరించండి"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"కుదించండి"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"విస్మరించండి"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-th/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-th/strings.xml
index c1453e9ba056..895143e5f5bb 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-th/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-th/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"ขยาย"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"ยุบ"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"ปิด"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-tr/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-tr/strings.xml
index b4a27d30f11a..6849bfeca192 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-tr/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-tr/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Genişlet"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Daralt"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Kapat"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-uk/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-uk/strings.xml
index 7670d0ea5a58..f4ab5e29bfd0 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-uk/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-uk/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Розгорнути"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Згорнути"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Закрити"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-ur/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-ur/strings.xml
index 82e0323d4206..07d0de5b879c 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-ur/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-ur/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"پھیلائیں"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"سکیڑیں"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"برخاست کریں"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-uz/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-uz/strings.xml
index e8a69b4bd62a..c444171fa6b4 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-uz/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-uz/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Yoyish"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Yopish"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Yopish"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles_expressive.xml
index ec67d068123a..a6fe6ec94e01 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles_expressive.xml
@@ -104,18 +104,6 @@
<item name="android:layout_width">match_parent</item>
</style>
- <style name="SettingslibTextAppearance.LinkableTextStyle.Expressive"
- parent="@style/TextAppearance.SettingsLib.LabelLarge">
- <item name="android:textColor">?android:attr/colorAccent</item>
- </style>
-
- <style name="SettingslibTextButtonStyle.Expressive"
- parent="@style/Widget.Material3Expressive.Button.TextButton.Icon">
- <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item>
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">wrap_content</item>
- </style>
-
<style name="SettingsLibCardStyle" parent="">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-vi/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-vi/strings.xml
index b115eb665f72..bfd2196f170b 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-vi/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-vi/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Mở rộng"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Thu gọn"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Đóng"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-zh-rCN/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-zh-rCN/strings.xml
index 779fff880ae1..1b949be434f5 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-zh-rCN/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"展开"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"收起"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"关闭"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-zh-rHK/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-zh-rHK/strings.xml
index 9d5de5b6f9c8..d3555b4e0ea3 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-zh-rHK/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"展開"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"收合"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"關閉"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-zh-rTW/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-zh-rTW/strings.xml
index 9d5de5b6f9c8..d3555b4e0ea3 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-zh-rTW/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"展開"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"收合"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"關閉"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-zu/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-zu/strings.xml
index c8d5ff080ee9..91d184614b32 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-zu/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-zu/strings.xml
@@ -19,6 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_expressive_text_expand" msgid="7520894876795775876">"Nweba"</string>
<string name="settingslib_expressive_text_collapse" msgid="5625043934702341576">"Goqa"</string>
- <!-- no translation found for settingslib_dismiss_button_content_description (6466433970910120385) -->
- <skip />
+ <string name="settingslib_dismiss_button_content_description" msgid="6466433970910120385">"Chitha"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
index f73e100906c8..686c1488fb62 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
@@ -250,4 +250,16 @@
<item name="android:lineHeight" tools:targetApi="28">16sp</item>
<item name="android:textAllCaps">false</item>
</style>
+
+ <style name="SettingslibTextAppearance.LinkableTextStyle.Expressive"
+ parent="@style/TextAppearance.SettingsLib.LabelLarge">
+ <item name="android:textColor">?android:attr/colorAccent</item>
+ </style>
+
+ <style name="SettingslibTextButtonStyle.Expressive"
+ parent="@style/Widget.Material3Expressive.Button.TextButton.Icon">
+ <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ </style>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index aa85c1aadb12..43e9cfdc7d7f 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -683,9 +683,9 @@
<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_photo_selector" msgid="433658323306627093">"Vælg billede"</string>
- <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"For mange forkerte forsøg. Dataene på denne enhed slettes."</string>
- <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"For mange forkerte forsøg. Denne bruger slettes."</string>
- <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"For mange forkerte forsøg. Denne arbejdsprofil og de tilhørende data slettes."</string>
+ <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"For mange mislykkede forsøg. Dataene på denne enhed slettes."</string>
+ <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"For mange mislykkede forsøg. Denne bruger slettes."</string>
+ <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"For mange mislykkede forsøg. Denne arbejdsprofil og de tilhørende data slettes."</string>
<string name="failed_attempts_now_wiping_dialog_dismiss" msgid="2749889771223578925">"Luk"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Enhedens standardindstilling"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiveret"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 7d5eece6c30e..84156429809b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -1087,8 +1087,9 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
private String generateRandomPassword() {
String randomUUID = UUID.randomUUID().toString();
- // first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
- return randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
+ // first 16 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
+ return randomUUID.substring(0, 8) + randomUUID.substring(9, 13) + randomUUID.substring(14,
+ 18);
}
private void registerContentObserver() {
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index d936a5c699c7..27a3cf1198b2 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -291,5 +291,6 @@ public class SecureSettings {
Settings.Secure.FACE_KEYGUARD_ENABLED,
Settings.Secure.FINGERPRINT_APP_ENABLED,
Settings.Secure.FINGERPRINT_KEYGUARD_ENABLED,
+ Settings.Secure.DUAL_SHADE,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 919c3c4721f2..8dca39fdc107 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -460,5 +460,6 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.FACE_KEYGUARD_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FINGERPRINT_APP_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FINGERPRINT_KEYGUARD_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.DUAL_SHADE, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 14b2dfe414a4..fc402d45c3ec 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -89,6 +89,7 @@ import java.io.OutputStream;
import java.time.DateTimeException;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
@@ -97,7 +98,6 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
-import java.util.HashMap;
import java.util.zip.CRC32;
/**
@@ -1753,8 +1753,8 @@ public class SettingsBackupAgent extends BackupAgentHelper {
if (previousDensity == null || previousDensity != newDensity) {
// From nothing to something is a change.
- DisplayDensityConfiguration.setForcedDisplayDensity(
- Display.DEFAULT_DISPLAY, newDensity);
+ DisplayDensityConfiguration.setForcedDisplayDensity(getBaseContext(),
+ info -> info.type == Display.TYPE_INTERNAL, newDensity);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 0f6311552de9..c98a741f8254 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -18,10 +18,13 @@ package com.android.providers.settings;
import static android.os.Process.FIRST_APPLICATION_UID;
+import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon;
+
import android.aconfig.Aconfig.flag_permission;
import android.aconfig.Aconfig.flag_state;
import android.aconfig.Aconfig.parsed_flag;
import android.aconfig.Aconfig.parsed_flags;
+import android.aconfigd.AconfigdFlagInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -65,14 +68,10 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
-import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.attribute.PosixFileAttributes;
-import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -84,18 +83,6 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
-// FOR ACONFIGD TEST MISSION AND ROLLOUT
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import android.util.proto.ProtoInputStream;
-import android.aconfigd.Aconfigd.StorageRequestMessage;
-import android.aconfigd.Aconfigd.StorageRequestMessages;
-import android.aconfigd.Aconfigd.StorageReturnMessage;
-import android.aconfigd.Aconfigd.StorageReturnMessages;
-import android.aconfigd.AconfigdClientSocket;
-import android.aconfigd.AconfigdFlagInfo;
-import android.aconfigd.AconfigdJavaUtils;
-import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon;
/**
* This class contains the state for one type of settings. It is responsible
* for saving the state asynchronously to an XML file after a mutation and
@@ -393,22 +380,6 @@ public class SettingsState {
getAllAconfigFlagsFromSettings(mAconfigDefaultFlags);
}
}
-
- if (isConfigSettingsKey(mKey)) {
- requests = handleBulkSyncToNewStorage(mAconfigDefaultFlags);
- }
- }
-
- if (enableAconfigStorageDaemon()) {
- if (isConfigSettingsKey(mKey)){
- AconfigdClientSocket localSocket = AconfigdJavaUtils.getAconfigdClientSocket();
- if (requests != null) {
- InputStream res = localSocket.send(requests.getBytes());
- if (res == null) {
- Slog.w(LOG_TAG, "Bulk sync request to acongid failed.");
- }
- }
- }
}
}
@@ -482,87 +453,6 @@ public class SettingsState {
return flag;
}
-
- // TODO(b/341764371): migrate aconfig flag push to GMS core
- @VisibleForTesting
- @GuardedBy("mLock")
- public ProtoOutputStream handleBulkSyncToNewStorage(
- Map<String, AconfigdFlagInfo> aconfigFlagMap) {
- // get marker or add marker if it does not exist
- Setting markerSetting = mSettings.get(BULK_SYNC_MARKER);
- int localCounter = 0;
- if (markerSetting == null) {
- markerSetting = new Setting(BULK_SYNC_MARKER, "0", false, "aconfig", "aconfig");
- mSettings.put(BULK_SYNC_MARKER, markerSetting);
- }
- try {
- localCounter = Integer.parseInt(markerSetting.value);
- } catch (NumberFormatException e) {
- // reset local counter
- markerSetting.value = "0";
- }
-
- if (enableAconfigStorageDaemon()) {
- Setting bulkSyncCounter = mSettings.get(BULK_SYNC_TRIGGER_COUNTER);
- int serverCounter = 0;
- if (bulkSyncCounter != null) {
- try {
- serverCounter = Integer.parseInt(bulkSyncCounter.value);
- } catch (NumberFormatException e) {
- // reset the local value of server counter
- bulkSyncCounter.value = "0";
- }
- }
-
- boolean shouldSync = localCounter < serverCounter;
- if (!shouldSync) {
- // CASE 1, flag is on, bulk sync marker true, nothing to do
- return null;
- } else {
- // CASE 2, flag is on, bulk sync marker false. Do following two tasks
- // (1) Do bulk sync here.
- // (2) After bulk sync, set marker to true.
-
- // first add storage reset request
- ProtoOutputStream requests = new ProtoOutputStream();
- AconfigdJavaUtils.writeResetStorageRequest(requests);
-
- // loop over all settings and add flag override requests
- for (AconfigdFlagInfo flag : aconfigFlagMap.values()) {
- // don't sync read_only flags
- if (!flag.getIsReadWrite()) {
- continue;
- }
-
- if (flag.getHasServerOverride()) {
- AconfigdJavaUtils.writeFlagOverrideRequest(
- requests,
- flag.getPackageName(),
- flag.getFlagName(),
- flag.getServerFlagValue(),
- StorageRequestMessage.SERVER_ON_REBOOT);
- }
-
- if (flag.getHasLocalOverride()) {
- AconfigdJavaUtils.writeFlagOverrideRequest(
- requests,
- flag.getPackageName(),
- flag.getFlagName(),
- flag.getLocalFlagValue(),
- StorageRequestMessage.LOCAL_ON_REBOOT);
- }
- }
-
- // mark sync has been done
- markerSetting.value = String.valueOf(serverCounter);
- scheduleWriteIfNeededLocked();
- return requests;
- }
- } else {
- return null;
- }
- }
-
@GuardedBy("mLock")
private void loadAconfigDefaultValuesLocked(List<String> filePaths) {
for (String fileName : filePaths) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 9aad5d5f8367..246aa7158cab 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -178,6 +178,7 @@ public class SettingsBackupTest {
Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT,
Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
+ Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_EXPERIENCE_FEATURES,
Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
Settings.Global.DEVELOPMENT_FORCE_RTL,
Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 6e7576631147..13eb1d4b2603 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -31,19 +31,20 @@ import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.Xml;
-import android.util.proto.ProtoOutputStream;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.modules.utils.TypedXmlSerializer;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
-
import com.google.common.base.Strings;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
@@ -52,11 +53,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class SettingsStateTest {
@@ -1085,124 +1081,6 @@ public class SettingsStateTest {
assertTrue(flag1.getHasLocalOverride());
}
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
- @Test
- @EnableFlags(com.android.aconfig_new_storage.Flags.FLAG_ENABLE_ACONFIG_STORAGE_DAEMON)
- public void testHandleBulkSyncWithAconfigdEnabled() {
- int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
- Object lock = new Object();
- SettingsState settingsState =
- new SettingsState(
- InstrumentationRegistry.getContext(),
- lock,
- mSettingsFile,
- configKey,
- SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED,
- Looper.getMainLooper());
-
- Map<String, AconfigdFlagInfo> flags = new HashMap<>();
- flags.put(
- "com.android.flags/flag1",
- AconfigdFlagInfo.newBuilder()
- .setPackageName("com.android.flags")
- .setFlagName("flag1")
- .setBootFlagValue("true")
- .setIsReadWrite(true)
- .build());
-
- flags.put(
- "com.android.flags/flag2",
- AconfigdFlagInfo.newBuilder()
- .setPackageName("com.android.flags")
- .setFlagName("flag2")
- .setBootFlagValue("true")
- .setIsReadWrite(false)
- .build());
-
- String bulkSyncMarker = "aconfigd_marker/bulk_synced";
- String bulkSyncCounter =
- "core_experiments_team_internal/" +
- "BulkSyncTriggerCounterFlag__bulk_sync_trigger_counter";
-
- synchronized (lock) {
- settingsState.insertSettingLocked(bulkSyncMarker, "0", null, false, "aconfig");
- settingsState.insertSettingLocked(bulkSyncCounter, "1", null, false,
- "com.google.android.platform.core_experiments_team_internal");
-
- // first bulk sync
- ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags);
- assertTrue(requests != null);
- String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
- assertEquals("1", value);
-
- // send time should no longer bulk sync
- requests = settingsState.handleBulkSyncToNewStorage(flags);
- assertNull(requests);
- value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
- assertEquals("1", value);
-
- // won't sync if the marker is string
- settingsState.insertSettingLocked(bulkSyncMarker, "true", null, false, "aconfig");
- settingsState.insertSettingLocked(bulkSyncCounter, "0", null, false,
- "com.google.android.platform.core_experiments_team_internal");
- requests = settingsState.handleBulkSyncToNewStorage(flags);
- assertNull(requests);
- value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
- assertEquals("0", value);
-
- // won't sync if the marker and counter value are the same
- settingsState.insertSettingLocked(bulkSyncMarker, "1", null, false, "aconfig");
- settingsState.insertSettingLocked(bulkSyncCounter, "1", null, false,
- "com.google.android.platform.core_experiments_team_internal");
- requests = settingsState.handleBulkSyncToNewStorage(flags);
- assertNull(requests);
- value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
- assertEquals("1", value);
- }
- }
-
- @Test
- @DisableFlags(com.android.aconfig_new_storage.Flags.FLAG_ENABLE_ACONFIG_STORAGE_DAEMON)
- public void testHandleBulkSyncWithAconfigdDisabled() {
- int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
- Object lock = new Object();
- SettingsState settingsState = new SettingsState(
- InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
- SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
-
- Map<String, AconfigdFlagInfo> flags = new HashMap<>();
- String bulkSyncMarker = "aconfigd_marker/bulk_synced";
- String bulkSyncCounter =
- "core_experiments_team_internal/" +
- "BulkSyncTriggerCounterFlag__bulk_sync_trigger_counter";
- synchronized (lock) {
- settingsState.insertSettingLocked("aconfigd_marker/bulk_synced",
- "true", null, false, "aconfig");
-
- // when aconfigd is off, should change the marker to false
- ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags);
- assertNull(requests);
- String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
- assertEquals("0", value);
-
- // marker started with false value, after call, it should remain false
- requests = settingsState.handleBulkSyncToNewStorage(flags);
- assertNull(requests);
- value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
- assertEquals("0", value);
-
- // won't sync
- settingsState.insertSettingLocked(bulkSyncMarker, "0", null, false, "aconfig");
- settingsState.insertSettingLocked(bulkSyncCounter, "1", null, false,
- "com.google.android.platform.core_experiments_team_internal");
- requests = settingsState.handleBulkSyncToNewStorage(flags);
- assertNull(requests);
- value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
- assertEquals("0", value);
- }
- }
-
@Test
public void testGetAllAconfigFlagsFromSettings() throws Exception {
final Object lock = new Object();
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b53198d8ae98..6491bf5c8f5b 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -392,6 +392,9 @@
<!-- To be able to decipher default applications for certain roles in shortcut helper -->
<uses-permission android:name="android.permission.MANAGE_DEFAULT_APPLICATIONS" />
+ <!-- To be able to set unrestricted system gesture exclusion rects -->
+ <uses-permission android:name="android.permission.SET_UNRESTRICTED_GESTURE_EXCLUSION"/>
+
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
@@ -548,10 +551,13 @@
android:exported="true" />
<service android:name=".wallpapers.GradientColorWallpaper"
- android:featureFlag="android.app.enable_connected_displays_wallpaper"
android:singleUser="true"
android:permission="android.permission.BIND_WALLPAPER"
- android:exported="true" />
+ android:exported="true">
+ <meta-data android:name="android.service.wallpaper"
+ android:resource="@xml/gradient_color_wallpaper">
+ </meta-data>
+ </service>
<activity android:name=".tuner.TunerActivity"
android:enabled="false"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
index 0ff856e0b91e..1d74774c7c11 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
@@ -5,7 +5,7 @@ package {
aconfig_declarations {
name: "com_android_a11y_menu_flags",
package: "com.android.systemui.accessibility.accessibilitymenu",
- container: "system",
+ container: "system_ext",
srcs: [
"accessibility.aconfig",
],
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
index 6d790114803a..bdf6d4242e68 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
@@ -1,5 +1,5 @@
package: "com.android.systemui.accessibility.accessibilitymenu"
-container: "system"
+container: "system_ext"
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index ac7d34b80aad..124b7421dd70 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -186,6 +186,16 @@ flag {
}
flag {
+ name: "notifications_pinned_hun_in_shade"
+ namespace: "systemui"
+ description: "Fixes displaying pinned HUNs in the Shade, making sure that their y and z positions are correct."
+ bug: "385041854"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "pss_app_selector_recents_split_screen"
namespace: "systemui"
description: "Allows recent apps selected for partial screenshare to be launched in split screen mode"
@@ -524,6 +534,16 @@ flag {
}
flag {
+ name: "indication_text_a11y_fix"
+ namespace: "systemui"
+ description: "add double shadow to the indication text at the bottom of the lock screen"
+ bug: "349297241"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "rest_to_unlock"
namespace: "systemui"
description: "Require prolonged touch for fingerprint authentication"
@@ -1774,6 +1794,13 @@ flag {
}
flag {
+ name: "bouncer_ui_revamp_2"
+ namespace: "systemui"
+ description: "Updates to button animations and font changes for bouncer, bouncer_ui_revamp will cover only the blur changes."
+ bug: "376491880"
+}
+
+flag {
name: "notification_shade_blur"
namespace: "systemui"
description: "Enables the new blur effect on the Notification Shade."
@@ -1825,6 +1852,13 @@ flag {
}
flag {
+ name: "double_tap_to_sleep"
+ namespace: "systemui"
+ description: "Enable Double Tap to Sleep"
+ bug: "385194612"
+}
+
+flag{
name: "gsf_bouncer"
namespace: "systemui"
description: "Applies GSF font styles to Bouncer surfaces."
@@ -1839,13 +1873,6 @@ flag {
}
flag {
- name: "glanceable_hub_shortcut_button"
- namespace: "systemui"
- description: "Adds a shortcut button to lockscreen to show glanceable hub."
- bug: "378173531"
-}
-
-flag {
name: "spatial_model_launcher_pushback"
namespace: "systemui"
description: "Implement the depth push scaling effect on Launcher when users pull down shade."
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
index a27bf8af1806..195b060932eb 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
@@ -464,13 +464,15 @@ private class NestedDraggableNode(
velocity: Velocity,
performFling: suspend (Velocity) -> Velocity,
): Velocity {
+ // Make sure we only use the velocity in this draggable orientation.
+ val orientationVelocity = velocity.toFloat().toVelocity()
return if (overscrollEffect != null) {
- overscrollEffect.applyToFling(velocity) { performFling(it) }
+ overscrollEffect.applyToFling(orientationVelocity) { performFling(it) }
// Effects always consume the whole velocity.
velocity
} else {
- performFling(velocity)
+ performFling(orientationVelocity)
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt
index 4ee6db3d516c..49e510791929 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt
@@ -23,7 +23,9 @@ import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.unit.Velocity
+import com.android.compose.ui.util.HorizontalSpaceVectorConverter
import com.android.compose.ui.util.SpaceVectorConverter
+import com.android.compose.ui.util.VerticalSpaceVectorConverter
import kotlin.math.abs
import kotlin.math.sign
import kotlinx.coroutines.CoroutineScope
@@ -40,13 +42,12 @@ interface ContentOverscrollEffect : OverscrollEffect {
}
open class BaseContentOverscrollEffect(
- orientation: Orientation,
private val animationScope: CoroutineScope,
private val animationSpec: AnimationSpec<Float>,
-) : ContentOverscrollEffect, SpaceVectorConverter by SpaceVectorConverter(orientation) {
-
+) : ContentOverscrollEffect {
/** The [Animatable] that holds the current overscroll value. */
private val animatable = Animatable(initialValue = 0f, visibilityThreshold = 0.5f)
+ private var lastConverter: SpaceVectorConverter? = null
override val overscrollDistance: Float
get() = animatable.value
@@ -59,6 +60,15 @@ open class BaseContentOverscrollEffect(
source: NestedScrollSource,
performScroll: (Offset) -> Offset,
): Offset {
+ val converter = converterOrNull(delta.x, delta.y) ?: return performScroll(delta)
+ return converter.applyToScroll(delta, source, performScroll)
+ }
+
+ private fun SpaceVectorConverter.applyToScroll(
+ delta: Offset,
+ source: NestedScrollSource,
+ performScroll: (Offset) -> Offset,
+ ): Offset {
val deltaForAxis = delta.toFloat()
// If we're currently overscrolled, and the user scrolls in the opposite direction, we need
@@ -106,6 +116,14 @@ open class BaseContentOverscrollEffect(
velocity: Velocity,
performFling: suspend (Velocity) -> Velocity,
) {
+ val converter = converterOrNull(velocity.x, velocity.y) ?: return
+ converter.applyToFling(velocity, performFling)
+ }
+
+ private suspend fun SpaceVectorConverter.applyToFling(
+ velocity: Velocity,
+ performFling: suspend (Velocity) -> Velocity,
+ ) {
// We launch a coroutine to ensure the fling animation starts after any pending [snapTo]
// animations have finished.
// This guarantees a smooth, sequential execution of animations on the overscroll value.
@@ -117,4 +135,35 @@ open class BaseContentOverscrollEffect(
}
}
}
+
+ protected fun requireConverter(): SpaceVectorConverter {
+ return checkNotNull(lastConverter) {
+ "lastConverter is null, make sure to call requireConverter() only when " +
+ "overscrollDistance != 0f"
+ }
+ }
+
+ private fun converterOrNull(x: Float, y: Float): SpaceVectorConverter? {
+ val converter: SpaceVectorConverter =
+ when {
+ x != 0f && y != 0f ->
+ error(
+ "BaseContentOverscrollEffect only supports single orientation scrolls " +
+ "and velocities"
+ )
+ x == 0f && y == 0f -> lastConverter ?: return null
+ x != 0f -> HorizontalSpaceVectorConverter
+ else -> VerticalSpaceVectorConverter
+ }
+
+ if (lastConverter != null) {
+ check(lastConverter == converter) {
+ "BaseContentOverscrollEffect should always be used in the same orientation"
+ }
+ } else {
+ lastConverter = converter
+ }
+
+ return converter
+ }
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt
index fc01c78f8c54..1bb8ae5019fb 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt
@@ -19,7 +19,7 @@ package com.android.compose.gesture.effect
import androidx.annotation.VisibleForTesting
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.foundation.OverscrollEffect
-import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.OverscrollFactory
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -37,24 +37,42 @@ import androidx.compose.ui.unit.dp
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
-/** An [OverscrollEffect] that offsets the content by the overscroll value. */
-class OffsetOverscrollEffect(
- orientation: Orientation,
- animationScope: CoroutineScope,
- animationSpec: AnimationSpec<Float>,
-) : BaseContentOverscrollEffect(orientation, animationScope, animationSpec) {
- private var _node: DelegatableNode = newNode()
- override val node: DelegatableNode
- get() = _node
+@Composable
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+fun rememberOffsetOverscrollEffect(
+ animationSpec: AnimationSpec<Float> = MaterialTheme.motionScheme.slowSpatialSpec()
+): OffsetOverscrollEffect {
+ val animationScope = rememberCoroutineScope()
+ return remember(animationScope, animationSpec) {
+ OffsetOverscrollEffect(animationScope, animationSpec)
+ }
+}
- fun newNode(): DelegatableNode {
- return object : Modifier.Node(), LayoutModifierNode {
- override fun onDetach() {
- super.onDetach()
- // TODO(b/379086317) Remove this workaround: avoid to reuse the same node.
- _node = newNode()
- }
+@Composable
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+fun rememberOffsetOverscrollEffectFactory(
+ animationSpec: AnimationSpec<Float> = MaterialTheme.motionScheme.slowSpatialSpec()
+): OverscrollFactory {
+ val animationScope = rememberCoroutineScope()
+ return remember(animationScope, animationSpec) {
+ OffsetOverscrollEffectFactory(animationScope, animationSpec)
+ }
+}
+data class OffsetOverscrollEffectFactory(
+ private val animationScope: CoroutineScope,
+ private val animationSpec: AnimationSpec<Float>,
+) : OverscrollFactory {
+ override fun createOverscrollEffect(): OverscrollEffect {
+ return OffsetOverscrollEffect(animationScope, animationSpec)
+ }
+}
+
+/** An [OverscrollEffect] that offsets the content by the overscroll value. */
+class OffsetOverscrollEffect(animationScope: CoroutineScope, animationSpec: AnimationSpec<Float>) :
+ BaseContentOverscrollEffect(animationScope, animationSpec) {
+ override val node: DelegatableNode =
+ object : Modifier.Node(), LayoutModifierNode {
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints,
@@ -62,11 +80,16 @@ class OffsetOverscrollEffect(
val placeable = measurable.measure(constraints)
return layout(placeable.width, placeable.height) {
val offsetPx = computeOffset(density = this@measure, overscrollDistance)
- placeable.placeRelativeWithLayer(position = offsetPx.toIntOffset())
+ if (offsetPx != 0) {
+ placeable.placeRelativeWithLayer(
+ with(requireConverter()) { offsetPx.toIntOffset() }
+ )
+ } else {
+ placeable.placeRelative(0, 0)
+ }
}
}
}
- }
companion object {
private val MaxDistance = 400.dp
@@ -80,18 +103,6 @@ class OffsetOverscrollEffect(
}
}
-@OptIn(ExperimentalMaterial3ExpressiveApi::class)
-@Composable
-fun rememberOffsetOverscrollEffect(
- orientation: Orientation,
- animationSpec: AnimationSpec<Float> = MaterialTheme.motionScheme.defaultSpatialSpec(),
-): OffsetOverscrollEffect {
- val animationScope = rememberCoroutineScope()
- return remember(orientation, animationScope, animationSpec) {
- OffsetOverscrollEffect(orientation, animationScope, animationSpec)
- }
-}
-
/** This converter lets you change a linear progress into a function of your choice. */
fun interface ProgressConverter {
fun convert(progress: Float): Float
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/ui/util/SpaceVectorConverter.kt b/packages/SystemUI/compose/core/src/com/android/compose/ui/util/SpaceVectorConverter.kt
index ca50e778d131..ac0e17a591dc 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/ui/util/SpaceVectorConverter.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/ui/util/SpaceVectorConverter.kt
@@ -37,11 +37,11 @@ interface SpaceVectorConverter {
fun SpaceVectorConverter(orientation: Orientation) =
when (orientation) {
- Orientation.Horizontal -> HorizontalConverter
- Orientation.Vertical -> VerticalConverter
+ Orientation.Horizontal -> HorizontalSpaceVectorConverter
+ Orientation.Vertical -> VerticalSpaceVectorConverter
}
-private data object HorizontalConverter : SpaceVectorConverter {
+data object HorizontalSpaceVectorConverter : SpaceVectorConverter {
override fun Offset.toFloat() = x
override fun Velocity.toFloat() = x
@@ -55,7 +55,7 @@ private data object HorizontalConverter : SpaceVectorConverter {
override fun Int.toIntOffset() = IntOffset(this, 0)
}
-private data object VerticalConverter : SpaceVectorConverter {
+data object VerticalSpaceVectorConverter : SpaceVectorConverter {
override fun Offset.toFloat() = y
override fun Velocity.toFloat() = y
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt
index 5a3f240deb44..e7c47fb56130 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt
@@ -55,10 +55,7 @@ class OffsetOverscrollEffectTest {
}
}
- private fun setupOverscrollableBox(
- scrollableOrientation: Orientation,
- overscrollEffectOrientation: Orientation = scrollableOrientation,
- ): LayoutInfo {
+ private fun setupOverscrollableBox(scrollableOrientation: Orientation): LayoutInfo {
val layoutSize: Dp = 200.dp
var touchSlop: Float by Delegates.notNull()
// The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
@@ -67,7 +64,7 @@ class OffsetOverscrollEffectTest {
rule.setContent {
density = LocalDensity.current
touchSlop = LocalViewConfiguration.current.touchSlop
- val overscrollEffect = rememberOffsetOverscrollEffect(overscrollEffectOrientation)
+ val overscrollEffect = rememberOffsetOverscrollEffect()
Box(
Modifier.overscroll(overscrollEffect)
@@ -102,11 +99,7 @@ class OffsetOverscrollEffectTest {
@Test
fun applyNoOffset_duringHorizontalOverscroll() {
- val info =
- setupOverscrollableBox(
- scrollableOrientation = Orientation.Vertical,
- overscrollEffectOrientation = Orientation.Horizontal,
- )
+ val info = setupOverscrollableBox(scrollableOrientation = Orientation.Vertical)
rule.onNodeWithTag(BOX_TAG).assertTopPositionInRootIsEqualTo(0.dp)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index fea34921b853..30dfa5bb826a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -37,6 +37,7 @@ import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
import com.android.systemui.communal.ui.compose.section.CommunalPopupSection
import com.android.systemui.communal.ui.compose.section.CommunalToDreamButtonSection
+import com.android.systemui.communal.ui.compose.section.HubOnboardingSection
import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
@@ -62,6 +63,7 @@ constructor(
private val communalPopupSection: CommunalPopupSection,
private val widgetSection: CommunalAppWidgetSection,
private val communalToDreamButtonSection: CommunalToDreamButtonSection,
+ private val hubOnboardingSection: HubOnboardingSection,
) {
@Composable
@@ -83,6 +85,7 @@ constructor(
modifier = Modifier.element(Communal.Elements.Grid),
contentScope = this@Content,
)
+ with(hubOnboardingSection) { BottomSheet() }
}
if (communalSettingsInteractor.isV2FlagEnabled()) {
Icon(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index 7a500805809d..c972d3e3cf15 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -44,6 +44,8 @@ import androidx.compose.ui.unit.round
import androidx.compose.ui.unit.toOffset
import androidx.compose.ui.unit.toSize
import com.android.systemui.Flags.communalWidgetResizing
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset
import com.android.systemui.communal.ui.compose.extensions.plus
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
@@ -105,6 +107,9 @@ internal constructor(
private var draggingItemDraggedDelta by mutableStateOf(Offset.Zero)
private var draggingItemInitialOffset by mutableStateOf(Offset.Zero)
+ private val spacer = CommunalContentModel.Spacer(CommunalContentSize.Responsive(1))
+ private var spacerIndex: Int? = null
+
private var previousTargetItemKey: Any? = null
internal val draggingItemOffset: Offset
@@ -140,6 +145,17 @@ internal constructor(
?.apply {
draggingItemKey = key as String
draggingItemInitialOffset = this.offset.toOffset()
+ // Add a spacer after the last widget if it is larger than the dragging widget.
+ // This allows overscrolling, enabling the dragging widget to be placed beyond it.
+ val lastWidget = contentListState.list.lastOrNull { it.isWidgetContent() }
+ if (
+ lastWidget != null &&
+ draggingItemLayoutInfo != null &&
+ lastWidget.size.span > draggingItemLayoutInfo!!.span
+ ) {
+ contentListState.list.add(spacer)
+ spacerIndex = contentListState.list.size - 1
+ }
return true
}
@@ -162,6 +178,11 @@ internal constructor(
previousTargetItemKey = null
draggingItemDraggedDelta = Offset.Zero
draggingItemInitialOffset = Offset.Zero
+ // Remove spacer, if any, when a drag gesture finishes.
+ spacerIndex?.let {
+ contentListState.list.removeAt(it)
+ spacerIndex = null
+ }
}
internal fun onDrag(offset: Offset, layoutDirection: LayoutDirection) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
index c7930549abe8..44c375d6ac5e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt
@@ -272,9 +272,8 @@ private fun calculateNumCellsWidth(width: Dp) =
}
private fun calculateNumCellsHeight(height: Dp) =
- // See https://developer.android.com/develop/ui/views/layout/use-window-size-classes
when {
- height >= 900.dp -> 3
+ height >= 1000.dp -> 3
height >= 480.dp -> 2
else -> 1
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/HubOnboardingSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/HubOnboardingSection.kt
new file mode 100644
index 000000000000..6943e9b00ed8
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/HubOnboardingSection.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.ui.compose.section
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.ChargingStation
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.communal.ui.viewmodel.HubOnboardingViewModel
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.ComponentSystemUIDialog
+import com.android.systemui.statusbar.phone.SystemUIDialogFactory
+import com.android.systemui.statusbar.phone.createBottomSheet
+import javax.inject.Inject
+
+class HubOnboardingSection
+@Inject
+constructor(
+ private val viewModelFactory: HubOnboardingViewModel.Factory,
+ private val dialogFactory: SystemUIDialogFactory,
+) {
+ @Composable
+ fun BottomSheet() {
+ val viewModel = rememberViewModel("HubOnboardingSection") { viewModelFactory.create() }
+ val shouldShowHubOnboarding by
+ viewModel.shouldShowHubOnboarding.collectAsStateWithLifecycle(false)
+
+ if (!shouldShowHubOnboarding) {
+ return
+ }
+
+ HubOnboardingBottomSheet(shouldShowBottomSheet = true, dialogFactory = dialogFactory) {
+ viewModel.onDismissed()
+ }
+ }
+}
+
+@Composable
+private fun HubOnboardingBottomSheet(
+ shouldShowBottomSheet: Boolean,
+ dialogFactory: SystemUIDialogFactory,
+ onDismiss: () -> Unit,
+) {
+ var dialog: ComponentSystemUIDialog? by remember { mutableStateOf(null) }
+ var dismissingDueToCancel by remember { mutableStateOf(false) }
+
+ DisposableEffect(shouldShowBottomSheet) {
+ if (shouldShowBottomSheet) {
+ dialog =
+ dialogFactory
+ .createBottomSheet(
+ content = { HubOnboardingBottomSheetContent { dialog?.dismiss() } },
+ isDraggable = true,
+ maxWidth = 627.dp,
+ )
+ .apply {
+ setOnDismissListener {
+ // Don't set the onboarding dismissed flag if the dismiss was due to a
+ // cancel. Note that a "dismiss" is something initiated by the user
+ // (e.g. swipe down or tapping outside), while a "cancel" is a dismiss
+ // not initiated by the user (e.g. timing out to dream). We only want
+ // to mark the bottom sheet as dismissed if the user explicitly
+ // dismissed it.
+ if (!dismissingDueToCancel) {
+ onDismiss()
+ }
+ }
+ setOnCancelListener { dismissingDueToCancel = true }
+ show()
+ }
+ }
+
+ onDispose {
+ dialog?.cancel()
+ dialog = null
+ }
+ }
+}
+
+@Composable
+private fun HubOnboardingBottomSheetContent(onButtonClicked: () -> Unit) {
+ val colors = MaterialTheme.colorScheme
+ Column(
+ modifier = Modifier.fillMaxWidth().padding(48.dp),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Icon(
+ imageVector = Icons.Outlined.ChargingStation,
+ contentDescription = null,
+ modifier = Modifier.size(32.dp),
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ Text(
+ text = stringResource(R.string.hub_onboarding_bottom_sheet_title),
+ style = MaterialTheme.typography.headlineMedium,
+ )
+ Spacer(modifier = Modifier.height(32.dp))
+ // TODO(b/388283881): Replace with correct animations and possibly add a content description
+ // if necessary.
+ Image(painter = painterResource(R.drawable.hub_onboarding_bg), contentDescription = null)
+ Spacer(modifier = Modifier.height(32.dp))
+ Text(
+ modifier = Modifier.width(300.dp),
+ text = stringResource(R.string.hub_onboarding_bottom_sheet_text),
+ textAlign = TextAlign.Center,
+ )
+ Spacer(modifier = Modifier.height(32.dp))
+ Button(
+ modifier = Modifier.align(Alignment.End),
+ colors =
+ ButtonDefaults.buttonColors(
+ containerColor = colors.primary,
+ contentColor = colors.onPrimary,
+ ),
+ onClick = onButtonClicked,
+ ) {
+ Text(
+ stringResource(R.string.hub_onboarding_bottom_sheet_action_button),
+ style = MaterialTheme.typography.labelLarge,
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
index 0ff567bf90ad..d8b3f742b447 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
@@ -19,12 +19,11 @@ package com.android.systemui.keyguard.ui.composable.section
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentScope
import com.android.systemui.keyguard.ui.viewmodel.KeyguardMediaViewModel
+import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.media.controls.ui.controller.MediaCarouselController
import com.android.systemui.media.controls.ui.view.MediaHost
@@ -38,7 +37,7 @@ class MediaCarouselSection
constructor(
private val mediaCarouselController: MediaCarouselController,
@param:Named(MediaModule.KEYGUARD) private val mediaHost: MediaHost,
- private val keyguardMediaViewModel: KeyguardMediaViewModel,
+ private val keyguardMediaViewModelFactory: KeyguardMediaViewModel.Factory,
) {
@Composable
@@ -46,7 +45,10 @@ constructor(
isShadeLayoutWide: Boolean,
modifier: Modifier = Modifier,
) {
- val isMediaVisible by keyguardMediaViewModel.isMediaVisible.collectAsStateWithLifecycle()
+ val viewModel =
+ rememberViewModel(traceName = "KeyguardMediaCarousel") {
+ keyguardMediaViewModelFactory.create()
+ }
val horizontalPadding =
if (isShadeLayoutWide) {
dimensionResource(id = R.dimen.notification_side_paddings)
@@ -55,7 +57,7 @@ constructor(
dimensionResource(id = R.dimen.notification_panel_margin_horizontal)
}
MediaCarousel(
- isVisible = isMediaVisible,
+ isVisible = viewModel.isMediaVisible,
mediaHost = mediaHost,
modifier = modifier.fillMaxWidth().padding(horizontal = horizontalPadding),
carouselController = mediaCarouselController,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
index 9eb1f68dd669..931134795a53 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
@@ -41,9 +41,10 @@ import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.composable.Overlay
-import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
import com.android.systemui.shade.ui.composable.OverlayShade
+import com.android.systemui.shade.ui.composable.OverlayShadeHeader
import com.android.systemui.shade.ui.composable.SingleShadeMeasurePolicy
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
@@ -60,6 +61,8 @@ constructor(
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
private val statusBarIconController: StatusBarIconController,
+ private val notificationIconContainerStatusBarViewBinder:
+ NotificationIconContainerStatusBarViewBinder,
private val shadeSession: SaveableSession,
private val stackScrollView: Lazy<NotificationScrollView>,
private val clockSection: DefaultClockSection,
@@ -79,7 +82,6 @@ constructor(
@Composable
override fun ContentScope.Content(modifier: Modifier) {
-
val notificationStackPadding = dimensionResource(id = R.dimen.notification_side_paddings)
val viewModel =
@@ -92,26 +94,29 @@ constructor(
}
OverlayShade(
+ isShadeLayoutWide = viewModel.isShadeLayoutWide,
panelAlignment = Alignment.TopStart,
modifier = modifier,
onScrimClicked = viewModel::onScrimClicked,
+ header = {
+ OverlayShadeHeader(
+ viewModelFactory = viewModel.shadeHeaderViewModelFactory,
+ createTintedIconManager = tintedIconManagerFactory::create,
+ createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
+ statusBarIconController = statusBarIconController,
+ notificationIconContainerStatusBarViewBinder =
+ notificationIconContainerStatusBarViewBinder,
+ modifier =
+ Modifier.element(NotificationsShade.Elements.StatusBar)
+ .layoutId(SingleShadeMeasurePolicy.LayoutId.ShadeHeader),
+ )
+ },
) {
Box {
Column {
if (viewModel.showHeader) {
val burnIn = rememberBurnIn(clockInteractor)
- CollapsedShadeHeader(
- viewModelFactory = viewModel.shadeHeaderViewModelFactory,
- createTintedIconManager = tintedIconManagerFactory::create,
- createBatteryMeterViewController =
- batteryMeterViewControllerFactory::create,
- statusBarIconController = statusBarIconController,
- modifier =
- Modifier.element(NotificationsShade.Elements.StatusBar)
- .layoutId(SingleShadeMeasurePolicy.LayoutId.ShadeHeader),
- )
-
with(clockSection) {
SmallClock(
burnInParams = burnIn.parameters,
@@ -126,16 +131,17 @@ constructor(
stackScrollView = stackScrollView.get(),
viewModel = placeholderViewModel,
maxScrimTop = { 0f },
+ shouldPunchHoleBehindScrim = false,
stackTopPadding = notificationStackPadding,
stackBottomPadding = notificationStackPadding,
- shouldPunchHoleBehindScrim = false,
shouldFillMaxSize = false,
shouldShowScrim = false,
supportNestedScrolling = false,
modifier = Modifier.fillMaxWidth(),
)
}
- // Communicates the bottom position of the drawable area within the shade to NSSL.
+ // Communicates the bottom position of the drawable area within the shade to
+ // NSSL.
NotificationStackCutoffGuideline(
stackScrollView = stackScrollView.get(),
viewModel = placeholderViewModel,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index 50bae8a094ad..3ec14a23421c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -37,6 +37,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.boundsInWindow
+import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
@@ -49,6 +50,7 @@ import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.notifications.ui.composable.NotificationsShade
import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
import com.android.systemui.qs.composefragment.ui.GridAnchor
import com.android.systemui.qs.flags.QsDetailedView
@@ -61,8 +63,11 @@ import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayActionsView
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayContentViewModel
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.composable.Overlay
-import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
import com.android.systemui.shade.ui.composable.OverlayShade
+import com.android.systemui.shade.ui.composable.OverlayShadeHeader
+import com.android.systemui.shade.ui.composable.QuickSettingsOverlayHeader
+import com.android.systemui.shade.ui.composable.SingleShadeMeasurePolicy
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
@@ -82,6 +87,8 @@ constructor(
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
private val statusBarIconController: StatusBarIconController,
+ private val notificationIconContainerStatusBarViewBinder:
+ NotificationIconContainerStatusBarViewBinder,
private val notificationStackScrollView: Lazy<NotificationScrollView>,
private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
) : Overlay {
@@ -108,43 +115,7 @@ constructor(
// set the bounds to null when the QuickSettings overlay disappears
DisposableEffect(Unit) { onDispose { viewModel.onPanelShapeChanged(null) } }
- OverlayShade(
- panelAlignment = Alignment.TopEnd,
- modifier = modifier,
- onScrimClicked = viewModel::onScrimClicked,
- ) {
- Column(
- modifier =
- Modifier.onPlaced { coordinates ->
- val boundsInWindow = coordinates.boundsInWindow()
- val shadeScrimBounds =
- ShadeScrimBounds(
- left = boundsInWindow.left,
- top = boundsInWindow.top,
- right = boundsInWindow.right,
- bottom = boundsInWindow.bottom,
- )
- val shape =
- ShadeScrimShape(
- bounds = shadeScrimBounds,
- topRadius = 0,
- bottomRadius = panelCornerRadius,
- )
- viewModel.onPanelShapeChanged(shape)
- }
- ) {
- if (viewModel.showHeader) {
- CollapsedShadeHeader(
- viewModelFactory = viewModel.shadeHeaderViewModelFactory,
- createTintedIconManager = tintedIconManagerFactory::create,
- createBatteryMeterViewController =
- batteryMeterViewControllerFactory::create,
- statusBarIconController = statusBarIconController,
- )
- }
- ShadeBody(viewModel = viewModel.quickSettingsContainerViewModel)
- }
-
+ Box(modifier = modifier) {
SnoozeableHeadsUpNotificationSpace(
stackScrollView = notificationStackScrollView.get(),
viewModel =
@@ -152,6 +123,58 @@ constructor(
notificationsPlaceholderViewModelFactory.create()
},
)
+ OverlayShade(
+ isShadeLayoutWide = viewModel.isShadeLayoutWide,
+ panelAlignment = Alignment.TopEnd,
+ onScrimClicked = viewModel::onScrimClicked,
+ header = {
+ OverlayShadeHeader(
+ viewModelFactory = viewModel.shadeHeaderViewModelFactory,
+ createTintedIconManager = tintedIconManagerFactory::create,
+ createBatteryMeterViewController =
+ batteryMeterViewControllerFactory::create,
+ statusBarIconController = statusBarIconController,
+ notificationIconContainerStatusBarViewBinder =
+ notificationIconContainerStatusBarViewBinder,
+ modifier =
+ Modifier.element(NotificationsShade.Elements.StatusBar)
+ .layoutId(SingleShadeMeasurePolicy.LayoutId.ShadeHeader),
+ )
+ },
+ ) {
+ ShadeBody(
+ viewModel = viewModel.quickSettingsContainerViewModel,
+ modifier =
+ Modifier.onPlaced { coordinates ->
+ val boundsInWindow = coordinates.boundsInWindow()
+ val shadeScrimBounds =
+ ShadeScrimBounds(
+ left = boundsInWindow.left,
+ top = boundsInWindow.top,
+ right = boundsInWindow.right,
+ bottom = boundsInWindow.bottom,
+ )
+ val shape =
+ ShadeScrimShape(
+ bounds = shadeScrimBounds,
+ topRadius = 0,
+ bottomRadius = panelCornerRadius,
+ )
+ viewModel.onPanelShapeChanged(shape)
+ },
+ header = {
+ if (viewModel.isShadeLayoutWide) {
+ QuickSettingsOverlayHeader(
+ viewModelFactory = viewModel.shadeHeaderViewModelFactory,
+ createBatteryMeterViewController =
+ batteryMeterViewControllerFactory::create,
+ modifier =
+ Modifier.padding(top = QuickSettingsShade.Dimensions.Padding),
+ )
+ }
+ },
+ )
+ }
}
}
}
@@ -166,7 +189,11 @@ sealed interface ShadeBodyState {
}
@Composable
-fun ContentScope.ShadeBody(viewModel: QuickSettingsContainerViewModel) {
+fun ContentScope.ShadeBody(
+ viewModel: QuickSettingsContainerViewModel,
+ modifier: Modifier = Modifier,
+ header: @Composable () -> Unit,
+) {
val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle()
val tileDetails =
if (QsDetailedView.isEnabled) viewModel.detailsViewModel.activeTileDetails else null
@@ -185,16 +212,19 @@ fun ContentScope.ShadeBody(viewModel: QuickSettingsContainerViewModel) {
EditMode(
viewModel = viewModel.editModeViewModel,
modifier =
- Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
+ modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
)
}
+
ShadeBodyState.TileDetails -> {
- TileDetails(viewModel.detailsViewModel)
+ TileDetails(modifier = modifier, viewModel.detailsViewModel)
}
+
else -> {
QuickSettingsLayout(
viewModel = viewModel,
- modifier = Modifier.sysuiResTag("quick_settings_panel"),
+ modifier = modifier.sysuiResTag("quick_settings_panel"),
+ header = header,
)
}
}
@@ -206,6 +236,7 @@ fun ContentScope.ShadeBody(viewModel: QuickSettingsContainerViewModel) {
fun ContentScope.QuickSettingsLayout(
viewModel: QuickSettingsContainerViewModel,
modifier: Modifier = Modifier,
+ header: @Composable () -> Unit,
) {
Column(
verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding),
@@ -217,6 +248,7 @@ fun ContentScope.QuickSettingsLayout(
bottom = QuickSettingsShade.Dimensions.Padding,
),
) {
+ header()
Toolbar(
modifier =
Modifier.fillMaxWidth().requiredHeight(QuickSettingsShade.Dimensions.ToolbarHeight),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 0ca7a6af01e0..6c0c5c7e49b9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -16,6 +16,7 @@
package com.android.systemui.scene.ui.composable
+import androidx.compose.foundation.LocalOverscrollFactory
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.layout.Box
@@ -44,6 +45,7 @@ import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.rememberMutableSceneTransitionLayoutState
+import com.android.compose.gesture.effect.rememberOffsetOverscrollEffectFactory
import com.android.systemui.lifecycle.rememberActivated
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.qs.ui.composable.QuickSettingsTheme
@@ -52,6 +54,7 @@ import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.view.SceneJankMonitor
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
+import com.android.systemui.shade.ui.composable.isFullWidthShade
import javax.inject.Provider
/**
@@ -150,6 +153,13 @@ fun SceneContainer(
}
}
+ // Overlays use the offset overscroll effect when shown on large screens, otherwise they
+ // stretch. All scenes use the OffsetOverscrollEffect.
+ val offsetOverscrollEffectFactory = rememberOffsetOverscrollEffectFactory()
+ val stretchOverscrollEffectFactory = checkNotNull(LocalOverscrollFactory.current)
+ val overlayEffectFactory =
+ if (isFullWidthShade()) stretchOverscrollEffectFactory else offsetOverscrollEffectFactory
+
// Inflate qsView here so that shade has the correct qqs height in the first measure pass after
// rebooting
if (
@@ -192,6 +202,7 @@ fun SceneContainer(
scene(
key = sceneKey,
userActions = userActionsByContentKey.getOrDefault(sceneKey, emptyMap()),
+ effectFactory = offsetOverscrollEffectFactory,
) {
// Activate the scene.
LaunchedEffect(scene) { scene.activate() }
@@ -208,6 +219,7 @@ fun SceneContainer(
overlay(
key = overlayKey,
userActions = userActionsByContentKey.getOrDefault(overlayKey, emptyMap()),
+ effectFactory = overlayEffectFactory,
) {
// Activate the overlay.
LaunchedEffect(overlay) { overlay.activate() }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index ffdf509174d5..fc59d40ec443 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -20,10 +20,8 @@ package com.android.systemui.shade.ui.composable
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.gestures.rememberScrollableState
-import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
@@ -44,59 +42,47 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
-import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
-import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexContentPicker
-import com.android.compose.gesture.effect.rememberOffsetOverscrollEffect
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.res.R
/** Renders a lightweight shade UI container, as an overlay. */
@Composable
fun ContentScope.OverlayShade(
+ isShadeLayoutWide: Boolean,
panelAlignment: Alignment,
onScrimClicked: () -> Unit,
modifier: Modifier = Modifier,
+ header: @Composable () -> Unit,
content: @Composable () -> Unit,
) {
- // TODO(b/384653288) This should be removed when b/378470603 is done.
- val idleEffect = rememberOffsetOverscrollEffect(Orientation.Vertical)
- Box(
- modifier
- .overscroll(idleEffect)
- .nestedScroll(
- remember {
- object : NestedScrollConnection {
- override suspend fun onPreFling(available: Velocity): Velocity {
- return available
- }
- }
- }
- )
- .scrollable(rememberScrollableState { 0f }, Orientation.Vertical, idleEffect)
- ) {
+ Box(modifier) {
Scrim(onClicked = onScrimClicked)
Box(modifier = Modifier.fillMaxSize().panelPadding(), contentAlignment = panelAlignment) {
Panel(
+ isShadeLayoutWide = isShadeLayoutWide,
modifier =
- Modifier.element(OverlayShade.Elements.Panel)
- .overscroll(verticalOverscrollEffect)
+ Modifier.overscroll(verticalOverscrollEffect)
+ .element(OverlayShade.Elements.Panel)
.panelSize(),
+ header = header,
content = content,
)
}
+
+ if (isShadeLayoutWide) {
+ header()
+ }
}
}
@@ -113,7 +99,12 @@ private fun ContentScope.Scrim(onClicked: () -> Unit, modifier: Modifier = Modif
}
@Composable
-private fun ContentScope.Panel(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
+private fun ContentScope.Panel(
+ isShadeLayoutWide: Boolean,
+ modifier: Modifier = Modifier,
+ header: @Composable () -> Unit,
+ content: @Composable () -> Unit,
+) {
Box(modifier = modifier.clip(OverlayShade.Shapes.RoundedCornerPanel)) {
Spacer(
modifier =
@@ -125,17 +116,22 @@ private fun ContentScope.Panel(modifier: Modifier = Modifier, content: @Composab
)
)
- // This content is intentionally rendered as a separate element from the background in order
- // to allow for more flexibility when defining transitions.
- content()
+ Column {
+ if (!isShadeLayoutWide) {
+ header()
+ }
+
+ // This content is intentionally rendered as a separate element from the background in
+ // order to allow for more flexibility when defining transitions.
+ content()
+ }
}
}
@Composable
private fun Modifier.panelSize(): Modifier {
- val widthSizeClass = LocalWindowSizeClass.current.widthSizeClass
return this.then(
- if (widthSizeClass == WindowWidthSizeClass.Compact) {
+ if (isFullWidthShade()) {
Modifier.fillMaxWidth()
} else {
Modifier.width(dimensionResource(id = R.dimen.shade_panel_width))
@@ -144,6 +140,12 @@ private fun Modifier.panelSize(): Modifier {
}
@Composable
+@ReadOnlyComposable
+internal fun isFullWidthShade(): Boolean {
+ return LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Compact
+}
+
+@Composable
private fun Modifier.panelPadding(): Modifier {
val widthSizeClass = LocalWindowSizeClass.current.widthSizeClass
val systemBars = WindowInsets.systemBarsIgnoringVisibility
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 3131b539c6af..c5d28adce601 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -26,6 +26,7 @@ import androidx.compose.foundation.interaction.collectIsHoveredAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
@@ -38,11 +39,11 @@ import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -50,6 +51,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Constraints
@@ -65,7 +67,6 @@ import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.animateElementFloatAsState
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.modifiers.thenIf
-import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.settingslib.Utils
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -77,10 +78,15 @@ import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.chipBackground
+import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.chipHighlighted
import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.onScrimDim
import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.CollapsedHeight
import com.android.systemui.shade.ui.composable.ShadeHeader.Values.ClockScale
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder
+import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
@@ -88,6 +94,7 @@ import com.android.systemui.statusbar.phone.ui.TintedIconManager
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel
import com.android.systemui.statusbar.policy.Clock
+import kotlinx.coroutines.launch
object ShadeHeader {
object Elements {
@@ -114,6 +121,12 @@ object ShadeHeader {
val ColorScheme.onScrimDim: Color
get() = Color.DarkGray
+
+ val ColorScheme.chipBackground: Color
+ get() = Color.DarkGray
+
+ val ColorScheme.chipHighlighted: Color
+ get() = Color.LightGray
}
object TestTags {
@@ -121,6 +134,7 @@ object ShadeHeader {
}
}
+/** The status bar that appears above the Shade scene on small screens */
@Composable
fun ContentScope.CollapsedShadeHeader(
viewModelFactory: ShadeHeaderViewModel.Factory,
@@ -131,9 +145,6 @@ fun ContentScope.CollapsedShadeHeader(
) {
val viewModel = rememberViewModel("CollapsedShadeHeader") { viewModelFactory.create() }
- val cutoutWidth = LocalDisplayCutout.current.width()
- val cutoutHeight = LocalDisplayCutout.current.height()
- val cutoutTop = LocalDisplayCutout.current.top
val cutoutLocation = LocalDisplayCutout.current.location
val horizontalPadding =
max(LocalScreenCornerRadius.current / 2f, Shade.Dimensions.HorizontalPadding)
@@ -146,123 +157,73 @@ fun ContentScope.CollapsedShadeHeader(
}
}
- val isLargeScreenLayout =
- LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Medium ||
- LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Expanded
+ val isShadeLayoutWide = viewModel.isShadeLayoutWide
val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle()
// This layout assumes it is globally positioned at (0, 0) and is the
// same size as the screen.
- Layout(
+ CutoutAwareShadeHeader(
modifier = modifier.sysuiResTag(ShadeHeader.TestTags.Root),
- contents =
- listOf(
- {
- Row(modifier = Modifier.padding(horizontal = horizontalPadding)) {
- Clock(
- scale = 1f,
+ startContent = {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(5.dp),
+ modifier = Modifier.padding(horizontal = horizontalPadding),
+ ) {
+ Clock(scale = 1f, viewModel = viewModel)
+ VariableDayDate(
+ viewModel = viewModel,
+ modifier = Modifier.element(ShadeHeader.Elements.CollapsedContentStart),
+ )
+ }
+ },
+ endContent = {
+ if (isPrivacyChipVisible) {
+ Box(
+ modifier =
+ Modifier.height(CollapsedHeight)
+ .fillMaxWidth()
+ .padding(horizontal = horizontalPadding)
+ ) {
+ PrivacyChip(
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.CenterEnd),
+ )
+ }
+ } else {
+ Row(
+ horizontalArrangement = Arrangement.End,
+ verticalAlignment = Alignment.CenterVertically,
+ modifier =
+ Modifier.element(ShadeHeader.Elements.CollapsedContentEnd)
+ .padding(horizontal = horizontalPadding),
+ ) {
+ if (isShadeLayoutWide) {
+ ShadeCarrierGroup(viewModel = viewModel)
+ }
+ SystemIconChip(viewModel = viewModel, isClickable = isShadeLayoutWide) {
+ StatusIcons(
viewModel = viewModel,
- modifier = Modifier.align(Alignment.CenterVertically),
+ createTintedIconManager = createTintedIconManager,
+ statusBarIconController = statusBarIconController,
+ useExpandedFormat = useExpandedTextFormat,
+ modifier = Modifier.padding(end = 6.dp).weight(1f, fill = false),
)
- Spacer(modifier = Modifier.width(5.dp))
- VariableDayDate(
+ BatteryIcon(
viewModel = viewModel,
- modifier =
- Modifier.element(ShadeHeader.Elements.CollapsedContentStart)
- .align(Alignment.CenterVertically),
+ createBatteryMeterViewController = createBatteryMeterViewController,
+ useExpandedFormat = useExpandedTextFormat,
+ modifier = Modifier.padding(vertical = 8.dp),
)
}
- },
- {
- if (isPrivacyChipVisible) {
- Box(
- modifier =
- Modifier.height(CollapsedHeight)
- .fillMaxWidth()
- .padding(horizontal = horizontalPadding)
- ) {
- PrivacyChip(
- viewModel = viewModel,
- modifier = Modifier.align(Alignment.CenterEnd),
- )
- }
- } else {
- Row(
- horizontalArrangement = Arrangement.End,
- modifier =
- Modifier.element(ShadeHeader.Elements.CollapsedContentEnd)
- .padding(horizontal = horizontalPadding),
- ) {
- if (isLargeScreenLayout) {
- ShadeCarrierGroup(
- viewModel = viewModel,
- modifier = Modifier.align(Alignment.CenterVertically),
- )
- }
- SystemIconContainer(
- viewModel = viewModel,
- isClickable = isLargeScreenLayout,
- modifier = Modifier.align(Alignment.CenterVertically),
- ) {
- StatusIcons(
- viewModel = viewModel,
- createTintedIconManager = createTintedIconManager,
- statusBarIconController = statusBarIconController,
- useExpandedFormat = useExpandedTextFormat,
- modifier =
- Modifier.align(Alignment.CenterVertically)
- .padding(end = 6.dp)
- .weight(1f, fill = false),
- )
- BatteryIcon(
- createBatteryMeterViewController =
- createBatteryMeterViewController,
- useExpandedFormat = useExpandedTextFormat,
- modifier = Modifier.align(Alignment.CenterVertically),
- )
- }
- }
- }
- },
- ),
- ) { measurables, constraints ->
- check(constraints.hasBoundedWidth)
- check(measurables.size == 2)
- check(measurables[0].size == 1)
- check(measurables[1].size == 1)
-
- val screenWidth = constraints.maxWidth
- val cutoutWidthPx = cutoutWidth.roundToPx()
- val height = max(cutoutHeight + (cutoutTop * 2), CollapsedHeight).roundToPx()
- val childConstraints = Constraints.fixed((screenWidth - cutoutWidthPx) / 2, height)
-
- val startMeasurable = measurables[0][0]
- val endMeasurable = measurables[1][0]
-
- val startPlaceable = startMeasurable.measure(childConstraints)
- val endPlaceable = endMeasurable.measure(childConstraints)
-
- layout(screenWidth, height) {
- when (cutoutLocation) {
- CutoutLocation.NONE,
- CutoutLocation.RIGHT -> {
- startPlaceable.placeRelative(x = 0, y = 0)
- endPlaceable.placeRelative(x = startPlaceable.width, y = 0)
- }
- CutoutLocation.CENTER -> {
- startPlaceable.placeRelative(x = 0, y = 0)
- endPlaceable.placeRelative(x = startPlaceable.width + cutoutWidthPx, y = 0)
- }
- CutoutLocation.LEFT -> {
- startPlaceable.placeRelative(x = cutoutWidthPx, y = 0)
- endPlaceable.placeRelative(x = startPlaceable.width + cutoutWidthPx, y = 0)
}
}
- }
- }
+ },
+ )
}
+/** The status bar that appears above the Quick Settings scene on small screens */
@Composable
fun ContentScope.ExpandedShadeHeader(
viewModelFactory: ShadeHeaderViewModel.Factory,
@@ -310,27 +271,24 @@ fun ContentScope.ExpandedShadeHeader(
}
}
Spacer(modifier = Modifier.width(5.dp))
- Row(modifier = Modifier.element(ShadeHeader.Elements.ExpandedContent)) {
- VariableDayDate(
- viewModel = viewModel,
- modifier = Modifier.widthIn(max = 90.dp).align(Alignment.CenterVertically),
- )
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.element(ShadeHeader.Elements.ExpandedContent),
+ ) {
+ VariableDayDate(viewModel = viewModel, modifier = Modifier.widthIn(max = 90.dp))
Spacer(modifier = Modifier.weight(1f))
- SystemIconContainer(viewModel = viewModel, isClickable = false) {
+ SystemIconChip(viewModel = viewModel) {
StatusIcons(
viewModel = viewModel,
createTintedIconManager = createTintedIconManager,
statusBarIconController = statusBarIconController,
useExpandedFormat = useExpandedFormat,
- modifier =
- Modifier.align(Alignment.CenterVertically)
- .padding(end = 6.dp)
- .weight(1f, fill = false),
+ modifier = Modifier.padding(end = 6.dp).weight(1f, fill = false),
)
BatteryIcon(
+ viewModel = viewModel,
useExpandedFormat = useExpandedFormat,
createBatteryMeterViewController = createBatteryMeterViewController,
- modifier = Modifier.align(Alignment.CenterVertically),
)
}
}
@@ -338,8 +296,183 @@ fun ContentScope.ExpandedShadeHeader(
}
}
+/**
+ * The status bar that appears above both the Notifications and Quick Settings shade overlays when
+ * overlay shade is enabled.
+ */
@Composable
-private fun ContentScope.Clock(scale: Float, viewModel: ShadeHeaderViewModel, modifier: Modifier) {
+fun ContentScope.OverlayShadeHeader(
+ viewModelFactory: ShadeHeaderViewModel.Factory,
+ createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
+ createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
+ statusBarIconController: StatusBarIconController,
+ notificationIconContainerStatusBarViewBinder: NotificationIconContainerStatusBarViewBinder,
+ modifier: Modifier = Modifier,
+) {
+ val viewModel = rememberViewModel("OverlayShadeHeader") { viewModelFactory.create() }
+
+ val horizontalPadding =
+ max(LocalScreenCornerRadius.current / 2f, Shade.Dimensions.HorizontalPadding)
+
+ val isShadeLayoutWide = viewModel.isShadeLayoutWide
+
+ val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle()
+
+ // This layout assumes it is globally positioned at (0, 0) and is the
+ // same size as the screen.
+ CutoutAwareShadeHeader(
+ modifier = modifier.sysuiResTag(ShadeHeader.TestTags.Root),
+ startContent = {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.padding(horizontal = horizontalPadding),
+ ) {
+ if (isShadeLayoutWide) {
+ Clock(
+ scale = 1f,
+ viewModel = viewModel,
+ modifier = Modifier.padding(horizontal = 4.dp),
+ )
+ Spacer(modifier = Modifier.width(5.dp))
+ }
+ NotificationIconChip(viewModel = viewModel) {
+ if (isShadeLayoutWide) {
+ NotificationIcons(
+ viewModel = viewModel,
+ notificationIconContainerStatusBarViewBinder =
+ notificationIconContainerStatusBarViewBinder,
+ modifier = Modifier.width(IntrinsicSize.Min).height(20.dp),
+ )
+ } else {
+ VariableDayDate(viewModel = viewModel)
+ }
+ }
+ }
+ },
+ endContent = {
+ Row(
+ horizontalArrangement = Arrangement.End,
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.padding(horizontal = horizontalPadding),
+ ) {
+ SystemIconChip(viewModel = viewModel, isClickable = true, showBackground = true) {
+ StatusIcons(
+ viewModel = viewModel,
+ createTintedIconManager = createTintedIconManager,
+ statusBarIconController = statusBarIconController,
+ useExpandedFormat = false,
+ highlightable = true,
+ modifier = Modifier.padding(end = 6.dp).weight(1f, fill = false),
+ )
+ BatteryIcon(
+ viewModel = viewModel,
+ createBatteryMeterViewController = createBatteryMeterViewController,
+ useExpandedFormat = false,
+ highlightable = true,
+ )
+ }
+ if (isPrivacyChipVisible) {
+ Box(
+ modifier =
+ Modifier.height(CollapsedHeight)
+ .fillMaxWidth()
+ .padding(horizontal = horizontalPadding)
+ ) {
+ PrivacyChip(
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.CenterEnd),
+ )
+ }
+ }
+ }
+ },
+ )
+}
+
+/** The header that appears at the top of the Quick Settings shade overlay. */
+@Composable
+fun ContentScope.QuickSettingsOverlayHeader(
+ viewModelFactory: ShadeHeaderViewModel.Factory,
+ createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
+ modifier: Modifier = Modifier,
+) {
+ val viewModel = rememberViewModel("QuickSettingsOverlayHeader") { viewModelFactory.create() }
+
+ Row(
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = modifier.fillMaxWidth(),
+ ) {
+ ShadeCarrierGroup(viewModel = viewModel)
+ BatteryIcon(
+ viewModel = viewModel,
+ createBatteryMeterViewController = createBatteryMeterViewController,
+ useExpandedFormat = true,
+ )
+ }
+}
+
+/*
+ * Places startContent and endContent according to the location of the display cutout.
+ * Assumes it is globally positioned at (0, 0) and the same size as the screen.
+ */
+@Composable
+private fun CutoutAwareShadeHeader(
+ modifier: Modifier = Modifier,
+ startContent: @Composable () -> Unit,
+ endContent: @Composable () -> Unit,
+) {
+ val cutoutWidth = LocalDisplayCutout.current.width()
+ val cutoutHeight = LocalDisplayCutout.current.height()
+ val cutoutTop = LocalDisplayCutout.current.top
+ val cutoutLocation = LocalDisplayCutout.current.location
+
+ Layout(
+ modifier = modifier.sysuiResTag(ShadeHeader.TestTags.Root),
+ contents = listOf(startContent, endContent),
+ ) { measurables, constraints ->
+ check(constraints.hasBoundedWidth)
+ check(measurables.size == 2)
+ check(measurables[0].size == 1)
+ check(measurables[1].size == 1)
+
+ val screenWidth = constraints.maxWidth
+ val cutoutWidthPx = cutoutWidth.roundToPx()
+ val height = max(cutoutHeight + (cutoutTop * 2), CollapsedHeight).roundToPx()
+ val childConstraints = Constraints.fixed((screenWidth - cutoutWidthPx) / 2, height)
+
+ val startMeasurable = measurables[0][0]
+ val endMeasurable = measurables[1][0]
+
+ val startPlaceable = startMeasurable.measure(childConstraints)
+ val endPlaceable = endMeasurable.measure(childConstraints)
+
+ layout(screenWidth, height) {
+ when (cutoutLocation) {
+ CutoutLocation.NONE,
+ CutoutLocation.RIGHT -> {
+ startPlaceable.placeRelative(x = 0, y = 0)
+ endPlaceable.placeRelative(x = startPlaceable.width, y = 0)
+ }
+ CutoutLocation.CENTER -> {
+ startPlaceable.placeRelative(x = 0, y = 0)
+ endPlaceable.placeRelative(x = startPlaceable.width + cutoutWidthPx, y = 0)
+ }
+ CutoutLocation.LEFT -> {
+ startPlaceable.placeRelative(x = cutoutWidthPx, y = 0)
+ endPlaceable.placeRelative(x = startPlaceable.width + cutoutWidthPx, y = 0)
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun ContentScope.Clock(
+ scale: Float,
+ viewModel: ShadeHeaderViewModel,
+ modifier: Modifier = Modifier,
+) {
val layoutDirection = LocalLayoutDirection.current
Element(key = ShadeHeader.Elements.Clock, modifier = modifier) {
@@ -374,28 +507,31 @@ private fun ContentScope.Clock(scale: Float, viewModel: ShadeHeaderViewModel, mo
@Composable
private fun BatteryIcon(
+ viewModel: ShadeHeaderViewModel,
createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
useExpandedFormat: Boolean,
+ highlightable: Boolean = false,
modifier: Modifier = Modifier,
) {
+ val localContext = LocalContext.current
+ val themedContext =
+ ContextThemeWrapper(localContext, R.style.Theme_SystemUI_QuickSettings_Header)
+ val primaryColor =
+ Utils.getColorAttrDefaultColor(themedContext, android.R.attr.textColorPrimary)
+ val inverseColor =
+ Utils.getColorAttrDefaultColor(themedContext, android.R.attr.textColorPrimaryInverse)
+
+ val isHighlighted = viewModel.highlightQuickSettingsIcons
+
AndroidView(
factory = { context ->
val batteryIcon = BatteryMeterView(context, null)
batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ON)
- val themedContext =
- ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings_Header)
- val fg = Utils.getColorAttrDefaultColor(themedContext, android.R.attr.textColorPrimary)
- val bg =
- Utils.getColorAttrDefaultColor(
- themedContext,
- android.R.attr.textColorPrimaryInverse,
- )
-
// [BatteryMeterView.updateColors] is an old method that was built to distinguish
// between dual-tone colors and single-tone. The current icon is only single-tone, so
// the final [fg] is the only one we actually need
- batteryIcon.updateColors(fg, bg, fg)
+ batteryIcon.updateColors(primaryColor, inverseColor, primaryColor)
val batteryMaterViewController =
createBatteryMeterViewController(batteryIcon, StatusBarLocation.QS)
@@ -414,6 +550,13 @@ private fun BatteryIcon(
BatteryMeterView.MODE_ON
}
)
+ if (highlightable) {
+ if (isHighlighted) {
+ batteryIcon.updateColors(primaryColor, inverseColor, inverseColor)
+ } else {
+ batteryIcon.updateColors(primaryColor, inverseColor, primaryColor)
+ }
+ }
},
modifier = modifier,
)
@@ -446,13 +589,51 @@ private fun ShadeCarrierGroup(viewModel: ShadeHeaderViewModel, modifier: Modifie
}
@Composable
+private fun NotificationIcons(
+ viewModel: ShadeHeaderViewModel,
+ notificationIconContainerStatusBarViewBinder: NotificationIconContainerStatusBarViewBinder,
+ modifier: Modifier = Modifier,
+) {
+ val scope = rememberCoroutineScope()
+
+ val isHighlighted = viewModel.highlightNotificationIcons
+
+ AndroidView(
+ factory = { context ->
+ NotificationIconContainer(context, null).also { view ->
+ view.setOverrideIconColor(true)
+ scope.launch {
+ notificationIconContainerStatusBarViewBinder.bindWhileAttached(
+ view = view,
+ displayId = context.displayId,
+ )
+ }
+ }
+ },
+ update = { it.setUseInverseOverrideIconColor(isHighlighted) },
+ modifier = modifier,
+ )
+}
+
+@Composable
private fun ContentScope.StatusIcons(
viewModel: ShadeHeaderViewModel,
createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
statusBarIconController: StatusBarIconController,
useExpandedFormat: Boolean,
+ highlightable: Boolean = false,
modifier: Modifier = Modifier,
) {
+ val localContext = LocalContext.current
+ val themedContext =
+ ContextThemeWrapper(localContext, R.style.Theme_SystemUI_QuickSettings_Header)
+ val primaryColor =
+ Utils.getColorAttrDefaultColor(themedContext, android.R.attr.textColorPrimary)
+ val inverseColor =
+ Utils.getColorAttrDefaultColor(themedContext, android.R.attr.textColorPrimaryInverse)
+
+ val isHighlighted = viewModel.highlightQuickSettingsIcons
+
val carrierIconSlots =
listOf(stringResource(id = com.android.internal.R.string.status_bar_mobile))
val cameraSlot = stringResource(id = com.android.internal.R.string.status_bar_camera)
@@ -466,19 +647,12 @@ private fun ContentScope.StatusIcons(
val isLocationIndicationEnabled by
viewModel.isLocationIndicationEnabled.collectAsStateWithLifecycle()
+ val iconContainer = remember { StatusIconContainer(themedContext, null) }
+ val iconManager = remember { createTintedIconManager(iconContainer, StatusBarLocation.QS) }
+
AndroidView(
factory = { context ->
- val themedContext =
- ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings_Header)
- val iconContainer = StatusIconContainer(themedContext, null)
- val iconManager = createTintedIconManager(iconContainer, StatusBarLocation.QS)
- iconManager.setTint(
- Utils.getColorAttrDefaultColor(themedContext, android.R.attr.textColorPrimary),
- Utils.getColorAttrDefaultColor(
- themedContext,
- android.R.attr.textColorPrimaryInverse,
- ),
- )
+ iconManager.setTint(primaryColor, inverseColor)
statusBarIconController.addIconGroup(iconManager)
iconContainer
@@ -511,35 +685,87 @@ private fun ContentScope.StatusIcons(
iconContainer.removeIgnoredSlot(micSlot)
iconContainer.removeIgnoredSlot(locationSlot)
}
+
+ if (highlightable) {
+ if (isHighlighted) {
+ iconManager.setTint(inverseColor, primaryColor)
+ } else {
+ iconManager.setTint(primaryColor, inverseColor)
+ }
+ }
},
modifier = modifier,
)
}
@Composable
-private fun SystemIconContainer(
+private fun NotificationIconChip(
viewModel: ShadeHeaderViewModel,
- isClickable: Boolean,
modifier: Modifier = Modifier,
content: @Composable RowScope.() -> Unit,
) {
val interactionSource = remember { MutableInteractionSource() }
- val isHovered by interactionSource.collectIsHoveredAsState()
+ val backgroundColor =
+ if (viewModel.highlightNotificationIcons) MaterialTheme.colorScheme.chipHighlighted
+ else MaterialTheme.colorScheme.chipBackground
+ Box(modifier = modifier) {
+ Row(
+ modifier =
+ Modifier.align(Alignment.CenterStart)
+ .clickable(
+ interactionSource = interactionSource,
+ indication = null,
+ onClick = { viewModel.onNotificationIconChipClicked() },
+ )
+ .thenIf(DualShade.isEnabled) {
+ Modifier.graphicsLayer {
+ shape = RoundedCornerShape(25.dp)
+ clip = true
+ }
+ .background(backgroundColor)
+ .padding(horizontal = 8.dp, vertical = 4.dp)
+ }
+ ) {
+ content()
+ }
+ }
+}
+
+@Composable
+private fun SystemIconChip(
+ viewModel: ShadeHeaderViewModel,
+ isClickable: Boolean = false,
+ showBackground: Boolean = false,
+ modifier: Modifier = Modifier,
+ content: @Composable RowScope.() -> Unit,
+) {
+ val interactionSource = remember { MutableInteractionSource() }
+ val isHovered by interactionSource.collectIsHoveredAsState()
val hoverModifier =
Modifier.clip(RoundedCornerShape(CollapsedHeight / 4))
.background(MaterialTheme.colorScheme.onScrimDim)
+ val backgroundColor =
+ if (viewModel.highlightQuickSettingsIcons) MaterialTheme.colorScheme.chipHighlighted
+ else MaterialTheme.colorScheme.chipBackground
Row(
+ verticalAlignment = Alignment.CenterVertically,
modifier =
modifier
- .height(CollapsedHeight)
- .padding(vertical = CollapsedHeight / 4)
+ .thenIf(showBackground) {
+ Modifier.graphicsLayer {
+ shape = RoundedCornerShape(25.dp)
+ clip = true
+ }
+ .background(backgroundColor)
+ .padding(horizontal = 8.dp, vertical = 4.dp)
+ }
.thenIf(isClickable) {
Modifier.clickable(
interactionSource = interactionSource,
indication = null,
- onClick = { viewModel.onSystemIconContainerClicked() },
+ onClick = { viewModel.onSystemIconChipClicked() },
)
}
.thenIf(isHovered) { hoverModifier },
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
index 931ff56e56cb..93eca86e15cf 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
@@ -6,17 +6,18 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.shadeHeaderText
+import com.android.compose.theme.colorAttr
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
@Composable
-fun VariableDayDate(
- viewModel: ShadeHeaderViewModel,
- modifier: Modifier = Modifier,
-) {
+fun VariableDayDate(viewModel: ShadeHeaderViewModel, modifier: Modifier = Modifier) {
val longerText = viewModel.longerDateText.collectAsStateWithLifecycle()
val shorterText = viewModel.shorterDateText.collectAsStateWithLifecycle()
+ val textColor =
+ if (viewModel.highlightNotificationIcons) colorAttr(android.R.attr.textColorPrimaryInverse)
+ else colorAttr(android.R.attr.textColorPrimary)
+
Layout(
contents =
listOf(
@@ -24,7 +25,7 @@ fun VariableDayDate(
Text(
text = longerText.value,
style = MaterialTheme.typography.bodyMedium,
- color = MaterialTheme.colorScheme.shadeHeaderText,
+ color = textColor,
maxLines = 1,
)
},
@@ -32,7 +33,7 @@ fun VariableDayDate(
Text(
text = shorterText.value,
style = MaterialTheme.typography.bodyMedium,
- color = MaterialTheme.colorScheme.shadeHeaderText,
+ color = textColor,
maxLines = 1,
)
},
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index ab3f6396e5c0..70ff47baa7a9 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -127,7 +127,14 @@ internal class DraggableHandler(
directionChangeSlop = layoutImpl.directionChangeSlop,
)
- return createSwipeAnimation(layoutImpl, result, isUpOrLeft, orientation, gestureContext)
+ return createSwipeAnimation(
+ layoutImpl,
+ result,
+ isUpOrLeft,
+ orientation,
+ gestureContext,
+ layoutImpl.decayAnimationSpec,
+ )
}
private fun resolveSwipeSource(startedPosition: Offset): SwipeSource.Resolved? {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
index 96d68ff03acd..41279d338896 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
@@ -18,19 +18,26 @@ package com.android.compose.animation.scene
import androidx.activity.BackEventCompat
import androidx.activity.compose.PredictiveBackHandler
+import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.snap
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Composable
+import androidx.compose.ui.util.fastCoerceIn
import com.android.compose.animation.scene.UserActionResult.ChangeScene
import com.android.compose.animation.scene.UserActionResult.HideOverlay
import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
import com.android.compose.animation.scene.UserActionResult.ShowOverlay
-import com.android.compose.animation.scene.transition.animateProgress
import com.android.mechanics.ProvidedGestureContext
import com.android.mechanics.spec.InputDirection
+import kotlin.coroutines.cancellation.CancellationException
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
@Composable
internal fun PredictiveBackHandler(
@@ -63,6 +70,7 @@ internal fun PredictiveBackHandler(
distance = 1f,
gestureContext =
ProvidedGestureContext(dragOffset = 0f, direction = InputDirection.Max),
+ decayAnimationSpec = layoutImpl.decayAnimationSpec,
)
animateProgress(
@@ -93,3 +101,63 @@ private fun UserActionResult.copy(
is ReplaceByOverlay -> copy(transitionKey = transitionKey)
}
}
+
+private suspend fun <T : ContentKey> animateProgress(
+ state: MutableSceneTransitionLayoutStateImpl,
+ animation: SwipeAnimation<T>,
+ progress: Flow<Float>,
+ commitSpec: AnimationSpec<Float>?,
+ cancelSpec: AnimationSpec<Float>?,
+ animationScope: CoroutineScope? = null,
+) {
+ suspend fun animateOffset(targetContent: T, spec: AnimationSpec<Float>?) {
+ if (state.transitionState != animation.contentTransition || animation.isAnimatingOffset()) {
+ return
+ }
+
+ animation.animateOffset(
+ initialVelocity = 0f,
+ targetContent = targetContent,
+
+ // Important: we have to specify a spec that correctly animates *progress* (low
+ // visibility threshold) and not *offset* (higher visibility threshold).
+ spec = spec ?: animation.contentTransition.transformationSpec.progressSpec,
+ )
+ }
+
+ coroutineScope {
+ val collectionJob = launch {
+ try {
+ progress.collectLatest { progress ->
+ // Progress based animation should never overscroll given that the
+ // absoluteDistance exposed to overscroll builders is always 1f and will not
+ // lead to any noticeable transformation.
+ animation.dragOffset = progress.fastCoerceIn(0f, 1f)
+ }
+
+ // Transition committed.
+ animateOffset(animation.toContent, commitSpec)
+ } catch (e: CancellationException) {
+ // Transition cancelled.
+ animateOffset(animation.fromContent, cancelSpec)
+ }
+ }
+
+ // Start the transition.
+ animationScope?.launch { startTransition(state, animation, collectionJob) }
+ ?: startTransition(state, animation, collectionJob)
+ }
+}
+
+private suspend fun <T : ContentKey> startTransition(
+ state: MutableSceneTransitionLayoutStateImpl,
+ animation: SwipeAnimation<T>,
+ progressCollectionJob: Job,
+) {
+ state.startTransition(animation.contentTransition)
+ // The transition is done. Cancel the collection in case the transition was finished
+ // because it was interrupted by another transition.
+ if (progressCollectionJob.isActive) {
+ progressCollectionJob.cancel()
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 184c2a28727b..72bb82bd41bb 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -17,6 +17,10 @@
package com.android.compose.animation.scene
import androidx.annotation.FloatRange
+import androidx.compose.animation.rememberSplineBasedDecay
+import androidx.compose.foundation.LocalOverscrollFactory
+import androidx.compose.foundation.OverscrollEffect
+import androidx.compose.foundation.OverscrollFactory
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
@@ -37,7 +41,6 @@ import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import com.android.compose.gesture.NestedScrollableBound
-import com.android.compose.gesture.effect.ContentOverscrollEffect
/**
* [SceneTransitionLayout] is a container that automatically animates its content whenever its state
@@ -82,12 +85,19 @@ interface SceneTransitionLayoutScope<out CS : ContentScope> {
* You can configure [userActions] so that swiping on this layout or navigating back will
* transition to a different scene.
*
+ * By default, [verticalOverscrollEffect][ContentScope.verticalOverscrollEffect] and
+ * [horizontalOverscrollEffect][ContentScope.horizontalOverscrollEffect] of this scene will be
+ * created using [LocalOverscrollFactory]. You can specify a non-null [effectFactory] to set up
+ * a custom factory that will be used by this scene and by any calls to
+ * rememberOverscrollEffect() inside the scene.
+ *
* Important: scene order along the z-axis follows call order. Calling scene(A) followed by
* scene(B) will mean that scene B renders after/above scene A.
*/
fun scene(
key: SceneKey,
userActions: Map<UserAction, UserActionResult> = emptyMap(),
+ effectFactory: OverscrollFactory? = null,
content: @Composable CS.() -> Unit,
)
@@ -108,6 +118,12 @@ interface SceneTransitionLayoutScope<out CS : ContentScope> {
* to prevent swipes from reaching other scenes or overlays behind this one. Clicking this
* protective layer will close the overlay.
*
+ * By default, [verticalOverscrollEffect][ContentScope.verticalOverscrollEffect] and
+ * [horizontalOverscrollEffect][ContentScope.horizontalOverscrollEffect] of this overlay will be
+ * created using [LocalOverscrollFactory]. You can specify a non-null [effectFactory] to set up
+ * a custom factory that will be used by this content and by any calls to
+ * rememberOverscrollEffect() inside the content.
+ *
* Important: overlays must be defined after all scenes. Overlay order along the z-axis follows
* call order. Calling overlay(A) followed by overlay(B) will mean that overlay B renders
* after/above overlay A.
@@ -118,6 +134,7 @@ interface SceneTransitionLayoutScope<out CS : ContentScope> {
mapOf(Back to UserActionResult.HideOverlay(key)),
alignment: Alignment = Alignment.Center,
isModal: Boolean = true,
+ effectFactory: OverscrollFactory? = null,
content: @Composable CS.() -> Unit,
)
}
@@ -262,7 +279,7 @@ interface ContentScope : BaseContentScope {
* The overscroll effect applied to the content in the vertical direction. This can be used to
* customize how the content behaves when the scene is over scrolled.
*
- * For example, you can use it with the `Modifier.overscroll()` modifier:
+ * You should use this effect exactly once with the `Modifier.overscroll()` modifier:
* ```kotlin
* @Composable
* fun ContentScope.MyScene() {
@@ -276,26 +293,9 @@ interface ContentScope : BaseContentScope {
* }
* ```
*
- * Or you can read the `overscrollDistance` value directly, if you need some custom overscroll
- * behavior:
- * ```kotlin
- * @Composable
- * fun ContentScope.MyScene() {
- * Box(
- * modifier = Modifier
- * .graphicsLayer {
- * // Translate half of the overscroll
- * translationY = verticalOverscrollEffect.overscrollDistance * 0.5f
- * }
- * ) {
- * // ... your content ...
- * }
- * }
- * ```
- *
* @see horizontalOverscrollEffect
*/
- val verticalOverscrollEffect: ContentOverscrollEffect
+ val verticalOverscrollEffect: OverscrollEffect
/**
* The overscroll effect applied to the content in the horizontal direction. This can be used to
@@ -303,7 +303,7 @@ interface ContentScope : BaseContentScope {
*
* @see verticalOverscrollEffect
*/
- val horizontalOverscrollEffect: ContentOverscrollEffect
+ val horizontalOverscrollEffect: OverscrollEffect
/**
* Animate some value at the content level.
@@ -746,7 +746,9 @@ internal fun SceneTransitionLayoutForTesting(
val density = LocalDensity.current
val directionChangeSlop = LocalViewConfiguration.current.touchSlop
val layoutDirection = LocalLayoutDirection.current
+ val defaultEffectFactory = checkNotNull(LocalOverscrollFactory.current)
val animationScope = rememberCoroutineScope()
+ val decayAnimationSpec = rememberSplineBasedDecay<Float>()
val layoutImpl = remember {
SceneTransitionLayoutImpl(
state = state as MutableSceneTransitionLayoutStateImpl,
@@ -761,13 +763,15 @@ internal fun SceneTransitionLayoutForTesting(
ancestors = ancestors,
lookaheadScope = lookaheadScope,
directionChangeSlop = directionChangeSlop,
+ defaultEffectFactory = defaultEffectFactory,
+ decayAnimationSpec = decayAnimationSpec,
)
.also { onLayoutImpl?.invoke(it) }
}
// TODO(b/317014852): Move this into the SideEffect {} again once STLImpl.scenes is not a
// SnapshotStateMap anymore.
- layoutImpl.updateContents(builder, layoutDirection)
+ layoutImpl.updateContents(builder, layoutDirection, defaultEffectFactory)
SideEffect {
if (state != layoutImpl.state) {
@@ -800,6 +804,7 @@ internal fun SceneTransitionLayoutForTesting(
layoutImpl.swipeSourceDetector = swipeSourceDetector
layoutImpl.swipeDetector = swipeDetector
layoutImpl.transitionInterceptionThreshold = transitionInterceptionThreshold
+ layoutImpl.decayAnimationSpec = decayAnimationSpec
}
layoutImpl.Content(modifier)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 16fd5b1e78e6..53996d25afdb 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -17,6 +17,8 @@
package com.android.compose.animation.scene
import androidx.annotation.VisibleForTesting
+import androidx.compose.animation.core.DecayAnimationSpec
+import androidx.compose.foundation.OverscrollFactory
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -81,6 +83,7 @@ internal class SceneTransitionLayoutImpl(
internal var swipeSourceDetector: SwipeSourceDetector,
internal var swipeDetector: SwipeDetector,
internal var transitionInterceptionThreshold: Float,
+ internal var decayAnimationSpec: DecayAnimationSpec<Float>,
builder: SceneTransitionLayoutScope<InternalContentScope>.() -> Unit,
/**
@@ -120,6 +123,7 @@ internal class SceneTransitionLayoutImpl(
*/
internal val ancestors: List<Ancestor> = emptyList(),
lookaheadScope: LookaheadScope? = null,
+ defaultEffectFactory: OverscrollFactory,
) {
/**
@@ -201,7 +205,7 @@ internal class SceneTransitionLayoutImpl(
private val nestedScrollConnection = object : NestedScrollConnection {}
init {
- updateContents(builder, layoutDirection)
+ updateContents(builder, layoutDirection, defaultEffectFactory)
// DraggableHandlerImpl must wait for the scenes to be initialized, in order to access the
// current scene (required for SwipeTransition).
@@ -209,14 +213,14 @@ internal class SceneTransitionLayoutImpl(
DraggableHandler(
layoutImpl = this,
orientation = Orientation.Horizontal,
- gestureEffectProvider = { content(it).scope.horizontalOverscrollGestureEffect },
+ gestureEffectProvider = { content(it).horizontalEffects.gestureEffect },
)
verticalDraggableHandler =
DraggableHandler(
layoutImpl = this,
orientation = Orientation.Vertical,
- gestureEffectProvider = { content(it).scope.verticalOverscrollGestureEffect },
+ gestureEffectProvider = { content(it).verticalEffects.gestureEffect },
)
// Make sure that the state is created on the same thread (most probably the main thread)
@@ -286,6 +290,7 @@ internal class SceneTransitionLayoutImpl(
internal fun updateContents(
builder: SceneTransitionLayoutScope<InternalContentScope>.() -> Unit,
layoutDirection: LayoutDirection,
+ defaultEffectFactory: OverscrollFactory,
) {
// Keep a reference of the current contents. After processing [builder], the contents that
// were not configured will be removed.
@@ -303,6 +308,7 @@ internal class SceneTransitionLayoutImpl(
override fun scene(
key: SceneKey,
userActions: Map<UserAction, UserActionResult>,
+ effectFactory: OverscrollFactory?,
content: @Composable InternalContentScope.() -> Unit,
) {
require(!overlaysDefined) { "all scenes must be defined before overlays" }
@@ -313,12 +319,14 @@ internal class SceneTransitionLayoutImpl(
val scene = scenes[key]
val globalZIndex =
Content.calculateGlobalZIndex(parentZIndex, ++zIndex, ancestors.size)
+ val factory = effectFactory ?: defaultEffectFactory
if (scene != null) {
// Update an existing scene.
scene.content = content
scene.userActions = resolvedUserActions
scene.zIndex = zIndex.toFloat()
scene.globalZIndex = globalZIndex
+ scene.maybeUpdateEffects(factory)
} else {
// New scene.
scenes[key] =
@@ -329,6 +337,7 @@ internal class SceneTransitionLayoutImpl(
resolvedUserActions,
zIndex.toFloat(),
globalZIndex,
+ factory,
)
}
}
@@ -338,6 +347,7 @@ internal class SceneTransitionLayoutImpl(
userActions: Map<UserAction, UserActionResult>,
alignment: Alignment,
isModal: Boolean,
+ effectFactory: OverscrollFactory?,
content: @Composable (InternalContentScope.() -> Unit),
) {
overlaysDefined = true
@@ -347,6 +357,7 @@ internal class SceneTransitionLayoutImpl(
val resolvedUserActions = resolveUserActions(key, userActions, layoutDirection)
val globalZIndex =
Content.calculateGlobalZIndex(parentZIndex, ++zIndex, ancestors.size)
+ val factory = effectFactory ?: defaultEffectFactory
if (overlay != null) {
// Update an existing overlay.
overlay.content = content
@@ -355,6 +366,7 @@ internal class SceneTransitionLayoutImpl(
overlay.userActions = resolvedUserActions
overlay.alignment = alignment
overlay.isModal = isModal
+ overlay.maybeUpdateEffects(factory)
} else {
// New overlay.
overlays[key] =
@@ -367,6 +379,7 @@ internal class SceneTransitionLayoutImpl(
globalZIndex,
alignment,
isModal,
+ factory,
)
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index 3bd37ad018b0..cb0d33cf5205 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -21,6 +21,8 @@ package com.android.compose.animation.scene
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.DecayAnimationSpec
+import androidx.compose.animation.core.calculateTargetValue
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.runtime.getValue
@@ -42,6 +44,7 @@ internal fun createSwipeAnimation(
orientation: Orientation,
distance: Float,
gestureContext: MutableDragOffsetGestureContext,
+ decayAnimationSpec: DecayAnimationSpec<Float>,
): SwipeAnimation<*> {
return createSwipeAnimation(
layoutState,
@@ -53,6 +56,7 @@ internal fun createSwipeAnimation(
error("Computing contentForUserActions requires a SceneTransitionLayoutImpl")
},
gestureContext = gestureContext,
+ decayAnimationSpec = decayAnimationSpec,
)
}
@@ -62,6 +66,7 @@ internal fun createSwipeAnimation(
isUpOrLeft: Boolean,
orientation: Orientation,
gestureContext: MutableDragOffsetGestureContext,
+ decayAnimationSpec: DecayAnimationSpec<Float>,
distance: Float = DistanceUnspecified,
): SwipeAnimation<*> {
var lastDistance = distance
@@ -106,6 +111,7 @@ internal fun createSwipeAnimation(
distance = ::distance,
contentForUserActions = { layoutImpl.contentForUserActions().key },
gestureContext = gestureContext,
+ decayAnimationSpec = decayAnimationSpec,
)
}
@@ -117,6 +123,7 @@ private fun createSwipeAnimation(
distance: (SwipeAnimation<*>) -> Float,
contentForUserActions: () -> ContentKey,
gestureContext: MutableDragOffsetGestureContext,
+ decayAnimationSpec: DecayAnimationSpec<Float>,
): SwipeAnimation<*> {
fun <T : ContentKey> swipeAnimation(fromContent: T, toContent: T): SwipeAnimation<T> {
return SwipeAnimation(
@@ -128,6 +135,7 @@ private fun createSwipeAnimation(
requiresFullDistanceSwipe = result.requiresFullDistanceSwipe,
distance = distance,
gestureContext = gestureContext,
+ decayAnimationSpec = decayAnimationSpec,
)
}
@@ -201,6 +209,7 @@ internal class SwipeAnimation<T : ContentKey>(
private val distance: (SwipeAnimation<T>) -> Float,
currentContent: T = fromContent,
private val gestureContext: MutableDragOffsetGestureContext,
+ private val decayAnimationSpec: DecayAnimationSpec<Float>,
) : MutableDragOffsetGestureContext by gestureContext {
/** The [TransitionState.Transition] whose implementation delegates to this [SwipeAnimation]. */
lateinit var contentTransition: TransitionState.Transition
@@ -367,20 +376,10 @@ internal class SwipeAnimation<T : ContentKey>(
check(isAnimatingOffset())
- val motionSpatialSpec = spec ?: layoutState.motionScheme.defaultSpatialSpec()
-
val velocityConsumed = CompletableDeferred<Float>()
offsetAnimationRunnable.complete {
- val result =
- animatable.animateTo(
- targetValue = targetOffset,
- animationSpec = motionSpatialSpec,
- initialVelocity = initialVelocity,
- )
-
- // We are no longer able to consume the velocity, the rest can be consumed by another
- // component in the hierarchy.
- velocityConsumed.complete(initialVelocity - result.endState.velocity)
+ val consumed = animateOffset(animatable, targetOffset, initialVelocity, spec)
+ velocityConsumed.complete(consumed)
// Wait for overscroll to finish so that the transition is removed from the STLState
// only after the overscroll is done, to avoid dropping frame right when the user lifts
@@ -391,6 +390,53 @@ internal class SwipeAnimation<T : ContentKey>(
return velocityConsumed.await()
}
+ private suspend fun animateOffset(
+ animatable: Animatable<Float, AnimationVector1D>,
+ targetOffset: Float,
+ initialVelocity: Float,
+ spec: AnimationSpec<Float>?,
+ ): Float {
+ val initialOffset = animatable.value
+ val decayOffset =
+ decayAnimationSpec.calculateTargetValue(
+ initialVelocity = initialVelocity,
+ initialValue = initialOffset,
+ )
+
+ val willDecayReachBounds =
+ when {
+ targetOffset > initialOffset -> decayOffset >= targetOffset
+ targetOffset < initialOffset -> decayOffset <= targetOffset
+ else -> true
+ }
+
+ if (willDecayReachBounds) {
+ val result = animatable.animateDecay(initialVelocity, decayAnimationSpec)
+ check(animatable.value == targetOffset) {
+ buildString {
+ appendLine(
+ "animatable.value = ${animatable.value} != $targetOffset = targetOffset"
+ )
+ appendLine(" initialOffset=$initialOffset")
+ appendLine(" targetOffset=$targetOffset")
+ appendLine(" initialVelocity=$initialVelocity")
+ appendLine(" decayOffset=$decayOffset")
+ }
+ }
+ return initialVelocity - result.endState.velocity
+ }
+
+ val motionSpatialSpec = spec ?: layoutState.motionScheme.defaultSpatialSpec()
+ animatable.animateTo(
+ targetValue = targetOffset,
+ animationSpec = motionSpatialSpec,
+ initialVelocity = initialVelocity,
+ )
+
+ // We consumed the whole velocity.
+ return initialVelocity
+ }
+
private fun canChangeContent(targetContent: ContentKey): Boolean {
return when (val transition = contentTransition) {
is TransitionState.Transition.ChangeScene ->
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
index 86201a9c0879..b7daaf4075ed 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
@@ -17,13 +17,12 @@
package com.android.compose.animation.scene.content
import android.annotation.SuppressLint
-import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.AnimationVector
-import androidx.compose.animation.core.TwoWayConverter
-import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.LocalOverscrollFactory
+import androidx.compose.foundation.OverscrollEffect
+import androidx.compose.foundation.OverscrollFactory
import androidx.compose.foundation.layout.Box
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
@@ -60,12 +59,10 @@ import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.animateSharedValueAsState
import com.android.compose.animation.scene.effect.GestureEffect
-import com.android.compose.animation.scene.effect.VisualEffect
import com.android.compose.animation.scene.element
import com.android.compose.animation.scene.modifiers.noResizeDuringTransitions
import com.android.compose.gesture.NestedScrollControlState
import com.android.compose.gesture.NestedScrollableBound
-import com.android.compose.gesture.effect.OffsetOverscrollEffect
import com.android.compose.gesture.nestedScrollController
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.ContainerState
@@ -81,11 +78,14 @@ internal sealed class Content(
actions: Map<UserAction.Resolved, UserActionResult>,
zIndex: Float,
globalZIndex: Long,
+ effectFactory: OverscrollFactory,
) {
private val nestedScrollControlState = NestedScrollControlState()
internal val scope = ContentScopeImpl(layoutImpl, content = this, nestedScrollControlState)
val containerState = ContainerState()
+ // Important: All fields in this class should be backed by State given that contents are updated
+ // directly during composition, outside of a SideEffect.
var content by mutableStateOf(content)
var targetSize by mutableStateOf(IntSize.Zero)
var userActions by mutableStateOf(actions)
@@ -143,16 +143,26 @@ internal sealed class Content(
}
}
+ private var lastFactory by mutableStateOf(effectFactory)
+ var verticalEffects by mutableStateOf(ContentEffects(effectFactory))
+ private set
+
+ var horizontalEffects by mutableStateOf(ContentEffects(effectFactory))
+ private set
+
@SuppressLint("NotConstructor")
@Composable
fun Content(modifier: Modifier = Modifier) {
+ // If this content has a custom factory, provide it to the content so that the factory is
+ // automatically used when calling rememberOverscrollEffect().
Box(
modifier
.zIndex(zIndex)
.approachLayout(
isMeasurementApproachInProgress = { layoutImpl.state.isTransitioning() }
) { measurable, constraints ->
- // TODO(b/353679003): Use the ModifierNode API to set this *before* the approach
+ // TODO(b/353679003): Use the ModifierNode API to set this *before* the
+ // approach
// pass is started.
targetSize = lookaheadSize
val placeable = measurable.measure(constraints)
@@ -163,11 +173,26 @@ internal sealed class Content(
}
.testTag(key.testTag)
) {
- scope.content()
+ CompositionLocalProvider(LocalOverscrollFactory provides lastFactory) {
+ scope.content()
+ }
}
}
fun areSwipesAllowed(): Boolean = nestedScrollControlState.isOuterScrollAllowed
+
+ fun maybeUpdateEffects(effectFactory: OverscrollFactory) {
+ if (effectFactory != lastFactory) {
+ lastFactory = effectFactory
+ verticalEffects = ContentEffects(effectFactory)
+ horizontalEffects = ContentEffects(effectFactory)
+ }
+ }
+}
+
+internal class ContentEffects(factory: OverscrollFactory) {
+ val overscrollEffect = factory.createOverscrollEffect()
+ val gestureEffect = GestureEffect(overscrollEffect)
}
internal class ContentScopeImpl(
@@ -183,34 +208,11 @@ internal class ContentScopeImpl(
override val lookaheadScope: LookaheadScope
get() = layoutImpl.lookaheadScope
- @OptIn(ExperimentalMaterial3ExpressiveApi::class)
- private val animationSpatialSpec =
- object : AnimationSpec<Float> {
- override fun <V : AnimationVector> vectorize(converter: TwoWayConverter<Float, V>) =
- layoutImpl.state.motionScheme.defaultSpatialSpec<Float>().vectorize(converter)
- }
-
- private val _verticalOverscrollEffect =
- OffsetOverscrollEffect(
- orientation = Orientation.Vertical,
- animationScope = layoutImpl.animationScope,
- animationSpec = animationSpatialSpec,
- )
-
- private val _horizontalOverscrollEffect =
- OffsetOverscrollEffect(
- orientation = Orientation.Horizontal,
- animationScope = layoutImpl.animationScope,
- animationSpec = animationSpatialSpec,
- )
-
- val verticalOverscrollGestureEffect = GestureEffect(_verticalOverscrollEffect)
-
- val horizontalOverscrollGestureEffect = GestureEffect(_horizontalOverscrollEffect)
-
- override val verticalOverscrollEffect = VisualEffect(_verticalOverscrollEffect)
+ override val verticalOverscrollEffect: OverscrollEffect
+ get() = content.verticalEffects.overscrollEffect
- override val horizontalOverscrollEffect = VisualEffect(_horizontalOverscrollEffect)
+ override val horizontalOverscrollEffect: OverscrollEffect
+ get() = content.horizontalEffects.overscrollEffect
override fun Modifier.element(key: ElementKey): Modifier {
return element(layoutImpl, content, key)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Overlay.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Overlay.kt
index 300de3f36b1f..4fbca430bc4b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Overlay.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Overlay.kt
@@ -16,6 +16,7 @@
package com.android.compose.animation.scene.content
+import androidx.compose.foundation.OverscrollFactory
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
@@ -39,7 +40,8 @@ internal class Overlay(
globalZIndex: Long,
alignment: Alignment,
isModal: Boolean,
-) : Content(key, layoutImpl, content, actions, zIndex, globalZIndex) {
+ effectFactory: OverscrollFactory,
+) : Content(key, layoutImpl, content, actions, zIndex, globalZIndex, effectFactory) {
var alignment by mutableStateOf(alignment)
var isModal by mutableStateOf(isModal)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Scene.kt
index 275341c7268b..7f57798fb1b3 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Scene.kt
@@ -16,6 +16,7 @@
package com.android.compose.animation.scene.content
+import androidx.compose.foundation.OverscrollFactory
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import com.android.compose.animation.scene.InternalContentScope
@@ -33,7 +34,8 @@ internal class Scene(
actions: Map<UserAction.Resolved, UserActionResult>,
zIndex: Float,
globalZIndex: Long,
-) : Content(key, layoutImpl, content, actions, zIndex, globalZIndex) {
+ effectFactory: OverscrollFactory,
+) : Content(key, layoutImpl, content, actions, zIndex, globalZIndex, effectFactory) {
override fun toString(): String {
return "Scene(key=$key)"
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/GestureEffect.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/GestureEffect.kt
index 2db45aa3dd58..a537c8764b0a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/GestureEffect.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/GestureEffect.kt
@@ -16,14 +16,14 @@
package com.android.compose.animation.scene.effect
+import androidx.compose.foundation.OverscrollEffect
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.unit.Velocity
-import com.android.compose.gesture.effect.ContentOverscrollEffect
/** An overscroll effect that ensures only a single fling animation is triggered. */
-internal class GestureEffect(private val delegate: ContentOverscrollEffect) :
- ContentOverscrollEffect by delegate {
+internal class GestureEffect(private val delegate: OverscrollEffect) :
+ OverscrollEffect by delegate {
private var shouldFling = false
override fun applyToScroll(
@@ -51,25 +51,3 @@ internal class GestureEffect(private val delegate: ContentOverscrollEffect) :
applyToFling(Velocity.Zero) { Velocity.Zero }
}
}
-
-/**
- * An overscroll effect that only applies visual effects and does not interfere with the actual
- * scrolling or flinging behavior.
- */
-internal class VisualEffect(private val delegate: ContentOverscrollEffect) :
- ContentOverscrollEffect by delegate {
- override fun applyToScroll(
- delta: Offset,
- source: NestedScrollSource,
- performScroll: (Offset) -> Offset,
- ): Offset {
- return performScroll(delta)
- }
-
- override suspend fun applyToFling(
- velocity: Velocity,
- performFling: suspend (Velocity) -> Velocity,
- ) {
- performFling(velocity)
- }
-}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt
deleted file mode 100644
index 819cec712808..000000000000
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compose.animation.scene.transition
-
-import androidx.annotation.FloatRange
-import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.ui.util.fastCoerceIn
-import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
-import com.android.compose.animation.scene.MutableSceneTransitionLayoutStateImpl
-import com.android.compose.animation.scene.OverlayKey
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.SwipeAnimation
-import com.android.compose.animation.scene.TransitionKey
-import com.android.compose.animation.scene.UserActionResult
-import com.android.compose.animation.scene.createSwipeAnimation
-import com.android.mechanics.ProvidedGestureContext
-import com.android.mechanics.spec.InputDirection
-import kotlin.coroutines.cancellation.CancellationException
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
-
-/**
- * Seek to the given [scene] using [progress].
- *
- * This will start a transition from the
- * [current scene][MutableSceneTransitionLayoutState.currentScene] to [scene], driven by the
- * progress in [progress]. Once [progress] stops emitting, we will animate progress to 1f (using
- * [animationSpec]) if it stopped normally or to 0f if it stopped with a
- * [kotlin.coroutines.cancellation.CancellationException].
- */
-suspend fun MutableSceneTransitionLayoutState.seekToScene(
- scene: SceneKey,
- @FloatRange(0.0, 1.0) progress: Flow<Float>,
- transitionKey: TransitionKey? = null,
- animationSpec: AnimationSpec<Float>? = null,
-) {
- require(scene != currentScene) {
- "seekToScene($scene) has to be called with a different scene than the current scene"
- }
-
- seek(UserActionResult.ChangeScene(scene, transitionKey), progress, animationSpec)
-}
-
-/**
- * Seek to show the given [overlay] using [progress].
- *
- * This will start a transition to show [overlay] from the
- * [current scene][MutableSceneTransitionLayoutState.currentScene], driven by the progress in
- * [progress]. Once [progress] stops emitting, we will animate progress to 1f (using
- * [animationSpec]) if it stopped normally or to 0f if it stopped with a
- * [kotlin.coroutines.cancellation.CancellationException].
- */
-suspend fun MutableSceneTransitionLayoutState.seekToShowOverlay(
- overlay: OverlayKey,
- @FloatRange(0.0, 1.0) progress: Flow<Float>,
- transitionKey: TransitionKey? = null,
- animationSpec: AnimationSpec<Float>? = null,
-) {
- require(overlay in currentOverlays) {
- "seekToShowOverlay($overlay) can be called only when the overlay is in currentOverlays"
- }
-
- seek(UserActionResult.ShowOverlay(overlay, transitionKey), progress, animationSpec)
-}
-
-/**
- * Seek to hide the given [overlay] using [progress].
- *
- * This will start a transition to hide [overlay] to the
- * [current scene][MutableSceneTransitionLayoutState.currentScene], driven by the progress in
- * [progress]. Once [progress] stops emitting, we will animate progress to 1f (using
- * [animationSpec]) if it stopped normally or to 0f if it stopped with a
- * [kotlin.coroutines.cancellation.CancellationException].
- */
-suspend fun MutableSceneTransitionLayoutState.seekToHideOverlay(
- overlay: OverlayKey,
- @FloatRange(0.0, 1.0) progress: Flow<Float>,
- transitionKey: TransitionKey? = null,
- animationSpec: AnimationSpec<Float>? = null,
-) {
- require(overlay !in currentOverlays) {
- "seekToHideOverlay($overlay) can be called only when the overlay is *not* in " +
- "currentOverlays"
- }
-
- seek(UserActionResult.HideOverlay(overlay, transitionKey), progress, animationSpec)
-}
-
-private suspend fun MutableSceneTransitionLayoutState.seek(
- result: UserActionResult,
- progress: Flow<Float>,
- animationSpec: AnimationSpec<Float>?,
-) {
- val layoutState =
- when (this) {
- is MutableSceneTransitionLayoutStateImpl -> this
- }
-
- val swipeAnimation =
- createSwipeAnimation(
- layoutState = layoutState,
- result = result,
-
- // We are animating progress, so distance is always 1f.
- distance = 1f,
-
- // The orientation and isUpOrLeft don't matter here given that they are only used during
- // overscroll, which is disabled for progress-based transitions.
- orientation = Orientation.Horizontal,
- isUpOrLeft = false,
- // There is no gesture information available here - animateProgress
- // will set the progress as the dragOffset.
- gestureContext = ProvidedGestureContext(0f, InputDirection.Max),
- )
-
- animateProgress(
- state = layoutState,
- animation = swipeAnimation,
- progress = progress,
- commitSpec = animationSpec,
- cancelSpec = animationSpec,
- )
-}
-
-internal suspend fun <T : ContentKey> animateProgress(
- state: MutableSceneTransitionLayoutStateImpl,
- animation: SwipeAnimation<T>,
- progress: Flow<Float>,
- commitSpec: AnimationSpec<Float>?,
- cancelSpec: AnimationSpec<Float>?,
- animationScope: CoroutineScope? = null,
-) {
- suspend fun animateOffset(targetContent: T, spec: AnimationSpec<Float>?) {
- if (state.transitionState != animation.contentTransition || animation.isAnimatingOffset()) {
- return
- }
-
- animation.animateOffset(
- initialVelocity = 0f,
- targetContent = targetContent,
-
- // Important: we have to specify a spec that correctly animates *progress* (low
- // visibility threshold) and not *offset* (higher visibility threshold).
- spec = spec ?: animation.contentTransition.transformationSpec.progressSpec,
- )
- }
-
- coroutineScope {
- val collectionJob = launch {
- try {
- progress.collectLatest { progress ->
- // Progress based animation should never overscroll given that the
- // absoluteDistance exposed to overscroll builders is always 1f and will not
- // lead to any noticeable transformation.
- animation.dragOffset = progress.fastCoerceIn(0f, 1f)
- }
-
- // Transition committed.
- animateOffset(animation.toContent, commitSpec)
- } catch (e: CancellationException) {
- // Transition cancelled.
- animateOffset(animation.fromContent, cancelSpec)
- }
- }
-
- // Start the transition.
- animationScope?.launch { startTransition(state, animation, collectionJob) }
- ?: startTransition(state, animation, collectionJob)
- }
-}
-
-private suspend fun <T : ContentKey> startTransition(
- state: MutableSceneTransitionLayoutStateImpl,
- animation: SwipeAnimation<T>,
- progressCollectionJob: Job,
-) {
- state.startTransition(animation.contentTransition)
- // The transition is done. Cancel the collection in case the transition was finished
- // because it was interrupted by another transition.
- if (progressCollectionJob.isActive) {
- progressCollectionJob.cancel()
- }
-}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index 68e85db2c80b..969003cb92f3 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -14,11 +14,17 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalMaterial3ExpressiveApi::class)
+
package com.android.compose.animation.scene
+import androidx.compose.animation.SplineBasedFloatDecayAnimationSpec
import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.generateDecayAnimationSpec
import androidx.compose.animation.core.spring
import androidx.compose.foundation.overscroll
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.MotionScheme
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
@@ -40,6 +46,7 @@ import com.android.compose.animation.scene.content.state.TransitionState.Compani
import com.android.compose.animation.scene.content.state.TransitionState.Transition
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.gesture.NestedDraggable
+import com.android.compose.gesture.effect.OffsetOverscrollEffectFactory
import com.android.compose.test.MonotonicClockTestScope
import com.android.compose.test.runMonotonicClockTest
import com.android.mechanics.spec.InputDirection
@@ -67,22 +74,25 @@ class DraggableHandlerTest {
canChangeScene = { canChangeScene(it) },
)
+ val defaultEffectFactory =
+ OffsetOverscrollEffectFactory(testScope, MotionScheme.standard().defaultSpatialSpec())
+
var layoutDirection = LayoutDirection.Rtl
set(value) {
field = value
- layoutImpl.updateContents(scenesBuilder, layoutDirection)
+ layoutImpl.updateContents(scenesBuilder, layoutDirection, defaultEffectFactory)
}
var mutableUserActionsA = mapOf(Swipe.Up to SceneB, Swipe.Down to SceneC)
set(value) {
field = value
- layoutImpl.updateContents(scenesBuilder, layoutDirection)
+ layoutImpl.updateContents(scenesBuilder, layoutDirection, defaultEffectFactory)
}
var mutableUserActionsB = mapOf(Swipe.Up to SceneC, Swipe.Down to SceneA)
set(value) {
field = value
- layoutImpl.updateContents(scenesBuilder, layoutDirection)
+ layoutImpl.updateContents(scenesBuilder, layoutDirection, defaultEffectFactory)
}
private val scenesBuilder: SceneTransitionLayoutScope<ContentScope>.() -> Unit = {
@@ -111,10 +121,11 @@ class DraggableHandlerTest {
val transitionInterceptionThreshold = 0.05f
val directionChangeSlop = 10f
+ private val density = Density(1f)
private val layoutImpl =
SceneTransitionLayoutImpl(
state = layoutState,
- density = Density(1f),
+ density = density,
layoutDirection = LayoutDirection.Ltr,
swipeSourceDetector = DefaultEdgeDetector,
swipeDetector = DefaultSwipeDetector,
@@ -125,6 +136,9 @@ class DraggableHandlerTest {
// work well with advanceUntilIdle(), which is used by some tests.
animationScope = testScope,
directionChangeSlop = directionChangeSlop,
+ defaultEffectFactory = defaultEffectFactory,
+ decayAnimationSpec =
+ SplineBasedFloatDecayAnimationSpec(density).generateDecayAnimationSpec(),
)
.apply { setContentsAndLayoutTargetSizeForTest(LAYOUT_SIZE) }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 8d0af9b57f2a..f625add0648b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -18,6 +18,7 @@ package com.android.compose.animation.scene
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.LocalOverscrollFactory
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.rememberScrollableState
import androidx.compose.foundation.gestures.scrollable
@@ -32,6 +33,7 @@ import androidx.compose.foundation.overscroll
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
@@ -72,6 +74,7 @@ import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.gesture.effect.OffsetOverscrollEffect
+import com.android.compose.gesture.effect.rememberOffsetOverscrollEffectFactory
import com.android.compose.test.assertSizeIsEqualTo
import com.android.compose.test.setContentAndCreateMainScope
import com.android.compose.test.transition
@@ -668,16 +671,20 @@ class ElementTest {
rule.setContent {
density = LocalDensity.current
touchSlop = LocalViewConfiguration.current.touchSlop
- SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) {
- scene(key = SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
- Spacer(Modifier.fillMaxSize())
- }
- scene(SceneB) {
- Spacer(
- Modifier.overscroll(verticalOverscrollEffect)
- .fillMaxSize()
- .element(TestElements.Foo)
- )
+ CompositionLocalProvider(
+ LocalOverscrollFactory provides rememberOffsetOverscrollEffectFactory()
+ ) {
+ SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) {
+ scene(key = SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Spacer(Modifier.fillMaxSize())
+ }
+ scene(SceneB) {
+ Spacer(
+ Modifier.overscroll(verticalOverscrollEffect)
+ .fillMaxSize()
+ .element(TestElements.Foo)
+ )
+ }
}
}
}
@@ -724,20 +731,24 @@ class ElementTest {
rule.setContent {
density = LocalDensity.current
touchSlop = LocalViewConfiguration.current.touchSlop
- SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) {
- scene(key = SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
- Spacer(
- Modifier.overscroll(verticalOverscrollEffect)
- .fillMaxSize()
- .element(TestElements.Foo)
- )
- }
- scene(SceneB) {
- Spacer(
- Modifier.overscroll(verticalOverscrollEffect)
- .fillMaxSize()
- .element(TestElements.Bar)
- )
+ CompositionLocalProvider(
+ LocalOverscrollFactory provides rememberOffsetOverscrollEffectFactory()
+ ) {
+ SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) {
+ scene(key = SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Spacer(
+ Modifier.overscroll(verticalOverscrollEffect)
+ .fillMaxSize()
+ .element(TestElements.Foo)
+ )
+ }
+ scene(SceneB) {
+ Spacer(
+ Modifier.overscroll(verticalOverscrollEffect)
+ .fillMaxSize()
+ .element(TestElements.Bar)
+ )
+ }
}
}
}
@@ -820,16 +831,20 @@ class ElementTest {
rule.setContent {
density = LocalDensity.current
touchSlop = LocalViewConfiguration.current.touchSlop
- SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) {
- scene(key = SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
- Spacer(Modifier.fillMaxSize())
- }
- scene(SceneB) {
- Spacer(
- Modifier.overscroll(verticalOverscrollEffect)
- .element(TestElements.Foo)
- .fillMaxSize()
- )
+ CompositionLocalProvider(
+ LocalOverscrollFactory provides rememberOffsetOverscrollEffectFactory()
+ ) {
+ SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) {
+ scene(key = SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Spacer(Modifier.fillMaxSize())
+ }
+ scene(SceneB) {
+ Spacer(
+ Modifier.overscroll(verticalOverscrollEffect)
+ .element(TestElements.Foo)
+ .fillMaxSize()
+ )
+ }
}
}
}
@@ -875,27 +890,31 @@ class ElementTest {
rule.setContent {
density = LocalDensity.current
touchSlop = LocalViewConfiguration.current.touchSlop
- SceneTransitionLayout(
- state = state,
- modifier = Modifier.size(layoutWidth, layoutHeight),
+ CompositionLocalProvider(
+ LocalOverscrollFactory provides rememberOffsetOverscrollEffectFactory()
) {
- scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
- Box(
- Modifier
- // A scrollable that does not consume the scroll gesture
- .scrollable(
- state = rememberScrollableState(consumeScrollDelta = { 0f }),
- orientation = Orientation.Vertical,
- )
- .fillMaxSize()
- )
- }
- scene(SceneB) {
- Spacer(
- Modifier.overscroll(verticalOverscrollEffect)
- .element(TestElements.Foo)
- .fillMaxSize()
- )
+ SceneTransitionLayout(
+ state = state,
+ modifier = Modifier.size(layoutWidth, layoutHeight),
+ ) {
+ scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Box(
+ Modifier
+ // A scrollable that does not consume the scroll gesture
+ .scrollable(
+ state = rememberScrollableState(consumeScrollDelta = { 0f }),
+ orientation = Orientation.Vertical,
+ )
+ .fillMaxSize()
+ )
+ }
+ scene(SceneB) {
+ Spacer(
+ Modifier.overscroll(verticalOverscrollEffect)
+ .element(TestElements.Foo)
+ .fillMaxSize()
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
index b4b328b14a78..04c762f43907 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
@@ -18,21 +18,29 @@ package com.android.compose.animation.scene
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.LocalOverscrollFactory
+import androidx.compose.foundation.OverscrollEffect
+import androidx.compose.foundation.OverscrollFactory
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberOverscrollEffect
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.SemanticsMatcher
@@ -47,6 +55,7 @@ import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipe
import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.TestOverlays.OverlayA
@@ -884,4 +893,65 @@ class OverlayTest {
assertThat(state.transitionState).hasCurrentScene(SceneA)
assertThat(state.transitionState).hasCurrentOverlays(OverlayC, OverlayD)
}
+
+ @Test
+ fun effectFactory() {
+ val effects = mutableSetOf<OverscrollEffect>()
+ var customFactory by mutableStateOf<OverscrollFactory?>(null)
+ rule.setContent {
+ CompositionLocalProvider(LocalOverscrollFactory provides DefaultEffectFactory) {
+ SceneTransitionLayout(
+ remember { MutableSceneTransitionLayoutStateForTests(SceneA) }
+ ) {
+ scene(SceneA, effectFactory = customFactory) {
+ val effect = checkNotNull(rememberOverscrollEffect())
+ SideEffect {
+ effects.add(effect)
+ effects.add(horizontalOverscrollEffect)
+ effects.add(verticalOverscrollEffect)
+ }
+ }
+ }
+ }
+ }
+
+ assertThat(effects.size).isEqualTo(3)
+ effects.forEach { assertThat(it).isInstanceOf(DefaultEffect::class.java) }
+
+ effects.clear()
+ customFactory = CustomEffectFactory
+ rule.waitForIdle()
+
+ assertThat(effects.size).isEqualTo(3)
+ effects.forEach { assertThat(it).isInstanceOf(CustomEffect::class.java) }
+ }
+
+ private abstract class NoOpEffect : OverscrollEffect {
+ override val isInProgress: Boolean = false
+
+ override suspend fun applyToFling(
+ velocity: Velocity,
+ performFling: suspend (Velocity) -> Velocity,
+ ) {
+ performFling(velocity)
+ }
+
+ override fun applyToScroll(
+ delta: Offset,
+ source: NestedScrollSource,
+ performScroll: (Offset) -> Offset,
+ ): Offset = performScroll(delta)
+ }
+
+ private class DefaultEffect : NoOpEffect()
+
+ private class CustomEffect : NoOpEffect()
+
+ private data object DefaultEffectFactory : OverscrollFactory {
+ override fun createOverscrollEffect(): OverscrollEffect = DefaultEffect()
+ }
+
+ private data object CustomEffectFactory : OverscrollFactory {
+ override fun createOverscrollEffect(): OverscrollEffect = CustomEffect()
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index c5e4061e834a..26f3c259dca9 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -24,18 +24,14 @@ import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.subjects.assertThat
-import com.android.compose.animation.scene.transition.seekToScene
import com.android.compose.test.TestSceneTransition
import com.android.compose.test.runMonotonicClockTest
import com.android.compose.test.transition
import com.google.common.truth.Truth.assertThat
-import kotlin.coroutines.cancellation.CancellationException
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.cancelAndJoin
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runCurrent
@@ -282,77 +278,6 @@ class SceneTransitionLayoutStateTest {
}
@Test
- fun seekToScene() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutStateForTests(SceneA)
- val progress = Channel<Float>()
-
- val job =
- launch(start = CoroutineStart.UNDISPATCHED) {
- state.seekToScene(SceneB, progress.consumeAsFlow())
- }
-
- val transition = assertThat(state.transitionState).isSceneTransition()
- assertThat(transition).hasFromScene(SceneA)
- assertThat(transition).hasToScene(SceneB)
- assertThat(transition).hasProgress(0f)
-
- // Change progress.
- progress.send(0.4f)
- assertThat(transition).hasProgress(0.4f)
-
- // Close the channel normally to confirm the transition.
- progress.close()
- job.join()
- assertThat(state.transitionState).isIdle()
- assertThat(state.transitionState).hasCurrentScene(SceneB)
- }
-
- @Test
- fun seekToScene_cancelled() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutStateForTests(SceneA)
- val progress = Channel<Float>()
-
- val job =
- launch(start = CoroutineStart.UNDISPATCHED) {
- state.seekToScene(SceneB, progress.consumeAsFlow())
- }
-
- val transition = assertThat(state.transitionState).isSceneTransition()
- assertThat(transition).hasFromScene(SceneA)
- assertThat(transition).hasToScene(SceneB)
- assertThat(transition).hasProgress(0f)
-
- // Change progress.
- progress.send(0.4f)
- assertThat(transition).hasProgress(0.4f)
-
- // Close the channel with a CancellationException to cancel the transition.
- progress.close(CancellationException())
- job.join()
- assertThat(state.transitionState).isIdle()
- assertThat(state.transitionState).hasCurrentScene(SceneA)
- }
-
- @Test
- fun seekToScene_interrupted() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutStateForTests(SceneA)
- val progress = Channel<Float>()
-
- val job =
- launch(start = CoroutineStart.UNDISPATCHED) {
- state.seekToScene(SceneB, progress.consumeAsFlow())
- }
-
- assertThat(state.transitionState).isSceneTransition()
-
- // Start a new transition, interrupting the seek transition.
- state.setTargetScene(SceneB, animationScope = this)
-
- // The previous job is cancelled and does not infinitely collect the progress.
- job.join()
- }
-
- @Test
fun replacedTransitionIsRemovedFromFinishedTransitions() = runTest {
val state = MutableSceneTransitionLayoutStateForTests(SceneA)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 1841d2ea6132..d2b61c0ab745 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -783,7 +783,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
underTest.reinflateViewFlipper(onViewInflatedCallback)
verify(viewFlipperController).clearViews()
verify(viewFlipperController)
- .asynchronouslyInflateView(any(), any(), onViewInflatedCallbackArgumentCaptor.capture())
+ .getSecurityView(any(), any(), onViewInflatedCallbackArgumentCaptor.capture())
onViewInflatedCallbackArgumentCaptor.value.onViewInflated(inputViewController)
verify(view).updateSecurityViewFlipper()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
index 7bb6ef1c8895..23e07b185a2e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -20,6 +20,7 @@ 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.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -129,23 +130,40 @@ public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
}
@Test
- public void asynchronouslyInflateView() {
- mKeyguardSecurityViewFlipperController.asynchronouslyInflateView(SecurityMode.PIN,
- mKeyguardSecurityCallback, null);
- verify(mAsyncLayoutInflater).inflate(anyInt(), eq(mView), any(
- AsyncLayoutInflater.OnInflateFinishedListener.class));
+ public void asynchronouslyInflateView_setNeedsInput() {
+ mKeyguardSecurityViewFlipperController.clearViews();
+ ArgumentCaptor<AsyncLayoutInflater.OnInflateFinishedListener> argumentCaptor =
+ ArgumentCaptor.forClass(AsyncLayoutInflater.OnInflateFinishedListener.class);
+ mKeyguardSecurityViewFlipperController.getSecurityView(SecurityMode.PIN,
+ mKeyguardSecurityCallback, controller -> {});
+ verify(mAsyncLayoutInflater).inflate(anyInt(), eq(mView), argumentCaptor.capture());
+ argumentCaptor.getValue().onInflateFinished(
+ LayoutInflater.from(getContext()).inflate(R.layout.keyguard_pin_view, null),
+ R.layout.keyguard_pin_view, mView);
}
@Test
- public void asynchronouslyInflateView_setNeedsInput() {
+ public void getSecurityView_multipleInvocations_callsAsyncInflateOnce() {
+ mKeyguardSecurityViewFlipperController.clearViews();
+ // Make 2 calls to get security view
+ var callback1 = mock(KeyguardSecurityViewFlipperController.OnViewInflatedCallback.class);
+ var callback2 = mock(KeyguardSecurityViewFlipperController.OnViewInflatedCallback.class);
+ mKeyguardSecurityViewFlipperController.getSecurityView(SecurityMode.PIN,
+ mKeyguardSecurityCallback, callback1);
+ mKeyguardSecurityViewFlipperController.getSecurityView(SecurityMode.PIN,
+ mKeyguardSecurityCallback, callback2);
+
+ // Verify inflation is called once...
ArgumentCaptor<AsyncLayoutInflater.OnInflateFinishedListener> argumentCaptor =
ArgumentCaptor.forClass(AsyncLayoutInflater.OnInflateFinishedListener.class);
- mKeyguardSecurityViewFlipperController.asynchronouslyInflateView(SecurityMode.PIN,
- mKeyguardSecurityCallback, null);
verify(mAsyncLayoutInflater).inflate(anyInt(), eq(mView), argumentCaptor.capture());
argumentCaptor.getValue().onInflateFinished(
- LayoutInflater.from(getContext()).inflate(R.layout.keyguard_password_view, null),
- R.layout.keyguard_password_view, mView);
+ LayoutInflater.from(getContext()).inflate(R.layout.keyguard_pin_view, null),
+ R.layout.keyguard_pin_view, mView);
+
+ // ... and both callbacks get invoked
+ verify(callback1).onViewInflated(mKeyguardInputViewController);
+ verify(callback2).onViewInflated(mKeyguardInputViewController);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt
index 3f4d3f8ba12a..205f94434970 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt
@@ -40,6 +40,7 @@ import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.clearInvocations
@ExperimentalCoroutinesApi
@SmallTest
@@ -65,11 +66,13 @@ class ConfigurationRepositoryImplTest : SysuiTestCase() {
testScope.backgroundScope,
displayUtils,
)
+ testScope.runCurrent()
}
@Test
fun onAnyConfigurationChange_updatesOnUiModeChanged() =
testScope.runTest {
+ clearInvocations(configurationController)
val lastAnyConfigurationChange by collectLastValue(underTest.onAnyConfigurationChange)
assertThat(lastAnyConfigurationChange).isNull()
@@ -85,6 +88,7 @@ class ConfigurationRepositoryImplTest : SysuiTestCase() {
@Test
fun onAnyConfigurationChange_updatesOnThemeChanged() =
testScope.runTest {
+ clearInvocations(configurationController)
val lastAnyConfigurationChange by collectLastValue(underTest.onAnyConfigurationChange)
assertThat(lastAnyConfigurationChange).isNull()
@@ -101,7 +105,7 @@ class ConfigurationRepositoryImplTest : SysuiTestCase() {
fun onMovedToDisplays_updatesOnMovedToDisplay() =
testScope.runTest {
val lastOnMovedToDisplay by collectLastValue(underTest.onMovedToDisplay)
- assertThat(lastOnMovedToDisplay).isNull()
+ assertThat(lastOnMovedToDisplay).isEqualTo(Display.DEFAULT_DISPLAY)
val configurationCallback = withArgCaptor {
verify(configurationController).addCallback(capture())
@@ -115,6 +119,7 @@ class ConfigurationRepositoryImplTest : SysuiTestCase() {
@Test
fun onAnyConfigurationChange_updatesOnConfigChanged() =
testScope.runTest {
+ clearInvocations(configurationController)
val lastAnyConfigurationChange by collectLastValue(underTest.onAnyConfigurationChange)
assertThat(lastAnyConfigurationChange).isNull()
@@ -130,6 +135,7 @@ class ConfigurationRepositoryImplTest : SysuiTestCase() {
@Test
fun onResolutionScale_updatesOnConfigurationChange() =
testScope.runTest {
+ clearInvocations(configurationController)
val scaleForResolution by collectLastValue(underTest.scaleForResolution)
assertThat(scaleForResolution).isEqualTo(displaySizeRatio)
@@ -149,6 +155,7 @@ class ConfigurationRepositoryImplTest : SysuiTestCase() {
@Test
fun onResolutionScale_nullMaxResolution() =
testScope.runTest {
+ clearInvocations(configurationController)
val scaleForResolution by collectLastValue(underTest.scaleForResolution)
runCurrent()
@@ -203,7 +210,7 @@ class ConfigurationRepositoryImplTest : SysuiTestCase() {
anyInt(),
anyInt(),
anyInt(),
- anyInt()
+ anyInt(),
)
)
.thenReturn(ratio)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
index 1f5e30ca4a0d..0d410cff5ff6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
@@ -88,6 +88,34 @@ class CommunalPrefsRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun isHubOnboardingDismissedValue_byDefault_isFalse() =
+ testScope.runTest {
+ val isHubOnboardingDismissed by
+ collectLastValue(underTest.isHubOnboardingDismissed(MAIN_USER))
+ assertThat(isHubOnboardingDismissed).isFalse()
+ }
+
+ @Test
+ fun isHubOnboardingDismissedValue_onSet_isTrue() =
+ testScope.runTest {
+ val isHubOnboardingDismissed by
+ collectLastValue(underTest.isHubOnboardingDismissed(MAIN_USER))
+
+ underTest.setHubOnboardingDismissed(MAIN_USER)
+ assertThat(isHubOnboardingDismissed).isTrue()
+ }
+
+ @Test
+ fun isHubOnboardingDismissedValue_onSetForDifferentUser_isStillFalse() =
+ testScope.runTest {
+ val isHubOnboardingDismissed by
+ collectLastValue(underTest.isHubOnboardingDismissed(MAIN_USER))
+
+ underTest.setHubOnboardingDismissed(SECONDARY_USER)
+ assertThat(isHubOnboardingDismissed).isFalse()
+ }
+
+ @Test
fun getSharedPreferences_whenFileRestored() =
testScope.runTest {
val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt
index 9a92f76f90c6..1fef6932ecca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt
@@ -74,6 +74,40 @@ class CommunalPrefsInteractorTest : SysuiTestCase() {
assertThat(isCtaDismissed).isFalse()
}
+ @Test
+ fun setHubOnboardingDismissed_currentUser() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ val isHubOnboardingDismissed by collectLastValue(underTest.isHubOnboardingDismissed)
+
+ assertThat(isHubOnboardingDismissed).isFalse()
+ underTest.setHubOnboardingDismissed(MAIN_USER)
+ assertThat(isHubOnboardingDismissed).isTrue()
+ }
+
+ @Test
+ fun setHubOnboardingDismissed_anotherUser() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ val isHubOnboardingDismissed by collectLastValue(underTest.isHubOnboardingDismissed)
+
+ assertThat(isHubOnboardingDismissed).isFalse()
+ underTest.setHubOnboardingDismissed(SECONDARY_USER)
+ assertThat(isHubOnboardingDismissed).isFalse()
+ }
+
+ @Test
+ fun isHubOnboardingDismissed_userSwitch() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ underTest.setHubOnboardingDismissed(MAIN_USER)
+ val isHubOnboardingDismissed by collectLastValue(underTest.isHubOnboardingDismissed)
+
+ assertThat(isHubOnboardingDismissed).isTrue()
+ setSelectedUser(SECONDARY_USER)
+ assertThat(isHubOnboardingDismissed).isFalse()
+ }
+
private suspend fun setSelectedUser(user: UserInfo) {
with(kosmos.fakeUserRepository) {
setUserInfos(listOf(user))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/HubOnboardingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/HubOnboardingInteractorTest.kt
new file mode 100644
index 000000000000..ef25dabb4c7f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/HubOnboardingInteractorTest.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.domain.interactor
+
+import android.content.pm.UserInfo
+import android.content.pm.UserInfo.FLAG_MAIN
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HubOnboardingInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val sceneInteractor = kosmos.sceneInteractor
+
+ private val underTest: HubOnboardingInteractor by lazy { kosmos.hubOnboardingInteractor }
+
+ @Test
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun setHubOnboardingDismissed() =
+ kosmos.runTest {
+ setSelectedUser(MAIN_USER)
+ val isHubOnboardingDismissed by
+ collectLastValue(fakeCommunalPrefsRepository.isHubOnboardingDismissed(MAIN_USER))
+
+ underTest.setHubOnboardingDismissed()
+
+ assertThat(isHubOnboardingDismissed).isTrue()
+ }
+
+ @Test
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun shouldShowHubOnboarding_falseWhenDismissed() =
+ kosmos.runTest {
+ setSelectedUser(MAIN_USER)
+ val shouldShowHubOnboarding by collectLastValue(underTest.shouldShowHubOnboarding)
+
+ fakeCommunalPrefsRepository.setHubOnboardingDismissed(MAIN_USER)
+
+ assertThat(shouldShowHubOnboarding).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun shouldShowHubOnboarding_falseWhenNotIdleOnCommunal() =
+ kosmos.runTest {
+ setSelectedUser(MAIN_USER)
+ val shouldShowHubOnboarding by collectLastValue(underTest.shouldShowHubOnboarding)
+
+ assertThat(shouldShowHubOnboarding).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun shouldShowHubOnboarding_trueWhenIdleOnCommunal() =
+ kosmos.runTest {
+ setSelectedUser(MAIN_USER)
+ val shouldShowHubOnboarding by collectLastValue(underTest.shouldShowHubOnboarding)
+
+ // Change to Communal scene.
+ setIdleScene(Scenes.Communal)
+
+ assertThat(shouldShowHubOnboarding).isFalse()
+ }
+
+ @Test
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun shouldShowHubOnboarding_falseWhenFlagDisabled() =
+ kosmos.runTest {
+ setSelectedUser(MAIN_USER)
+ val shouldShowHubOnboarding by collectLastValue(underTest.shouldShowHubOnboarding)
+
+ // Change to Communal scene.
+ setIdleScene(Scenes.Communal)
+
+ assertThat(shouldShowHubOnboarding).isFalse()
+ }
+
+ private fun setIdleScene(scene: SceneKey) {
+ sceneInteractor.changeScene(scene, "test")
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(scene))
+ sceneInteractor.setTransitionState(transitionState)
+ }
+
+ private suspend fun setSelectedUser(user: UserInfo) {
+ with(kosmos.fakeUserRepository) {
+ setUserInfos(listOf(user))
+ setSelectedUserInfo(user)
+ }
+ kosmos.fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
+ }
+
+ companion object {
+ val MAIN_USER = UserInfo(0, "main", FLAG_MAIN)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt
new file mode 100644
index 000000000000..0df8834618d5
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.posturing.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.posturing.data.repository.fake
+import com.android.systemui.communal.posturing.data.repository.posturingRepository
+import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PosturingInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+
+ private val underTest by lazy { kosmos.posturingInteractor }
+
+ @Test
+ fun testNoDebugOverride() =
+ kosmos.runTest {
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+ assertThat(postured).isTrue()
+ }
+
+ @Test
+ fun testOverriddenByDebugValue() =
+ kosmos.runTest {
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ underTest.setValueForDebug(PosturedState.NotPostured)
+ posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+
+ // Repository value is overridden by debug value
+ assertThat(postured).isFalse()
+
+ underTest.setValueForDebug(PosturedState.Unknown)
+ assertThat(postured).isTrue()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/HubOnboardingViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/HubOnboardingViewModelTest.kt
new file mode 100644
index 000000000000..712d26275000
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/HubOnboardingViewModelTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.ui.viewmodel
+
+import android.content.pm.UserInfo
+import android.content.pm.UserInfo.FLAG_MAIN
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
+import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+@RunWith(AndroidJUnit4::class)
+class HubOnboardingViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val underTest: HubOnboardingViewModel by lazy { kosmos.hubOnboardingViewModel }
+
+ @Before
+ fun setUp() {
+ kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+ underTest.activateIn(kosmos.testScope)
+ }
+
+ @Test
+ fun onDismissed_setsDismissedTrue() =
+ kosmos.runTest {
+ setSelectedUser(MAIN_USER)
+
+ val isHubOnboardingDismissed by
+ collectLastValue(fakeCommunalPrefsRepository.isHubOnboardingDismissed(MAIN_USER))
+
+ underTest.onDismissed()
+
+ assertThat(isHubOnboardingDismissed).isTrue()
+ }
+
+ private suspend fun setSelectedUser(user: UserInfo) {
+ with(kosmos.fakeUserRepository) {
+ setUserInfos(listOf(user))
+ setSelectedUserInfo(user)
+ }
+ kosmos.fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
+ }
+
+ companion object {
+ val MAIN_USER = UserInfo(0, "main", FLAG_MAIN)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt
deleted file mode 100644
index ac06a3b9293c..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigTest.kt
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.keyguard.data.quickaffordance
-
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.FlagsParameterization
-import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.communalSceneRepository
-import com.android.systemui.communal.domain.interactor.setCommunalV2Available
-import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled
-import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.flags.parameterizeSceneContainerFlag
-import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.collectLastValue
-import com.android.systemui.kosmos.runCurrent
-import com.android.systemui.kosmos.runTest
-import com.android.systemui.scene.data.repository.sceneContainerRepository
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.MockitoAnnotations
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
-
-@SmallTest
-@EnableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
-@RunWith(ParameterizedAndroidJunit4::class)
-class GlanceableHubQuickAffordanceConfigTest(flags: FlagsParameterization?) : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val Kosmos.underTest by Kosmos.Fixture { glanceableHubQuickAffordanceConfig }
-
- init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
- }
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- // Access the class immediately so that flows are instantiated.
- // GlanceableHubQuickAffordanceConfig accesses StateFlow.value directly so we need the flows
- // to start flowing before runCurrent is called in the tests.
- kosmos.underTest
- }
-
- @Test
- fun lockscreenState_whenGlanceableHubEnabled_returnsVisible() =
- kosmos.runTest {
- kosmos.setCommunalV2Available(true)
- runCurrent()
-
- val lockScreenState by collectLastValue(underTest.lockScreenState)
-
- assertThat(lockScreenState)
- .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
- }
-
- @Test
- fun lockscreenState_whenGlanceableHubDisabled_returnsHidden() =
- kosmos.runTest {
- setCommunalV2Enabled(false)
- val lockScreenState by collectLastValue(underTest.lockScreenState)
- runCurrent()
-
- assertThat(lockScreenState)
- .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
- }
-
- @Test
- fun lockscreenState_whenGlanceableHubNotAvailable_returnsHidden() =
- kosmos.runTest {
- // Hub is enabled, but not available.
- setCommunalV2Enabled(true)
- fakeKeyguardRepository.setKeyguardShowing(false)
- val lockScreenState by collectLastValue(underTest.lockScreenState)
- runCurrent()
-
- assertThat(lockScreenState)
- .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
- }
-
- @Test
- fun pickerScreenState_whenGlanceableHubEnabled_returnsDefault() =
- kosmos.runTest {
- setCommunalV2Enabled(true)
- runCurrent()
-
- assertThat(underTest.getPickerScreenState())
- .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default())
- }
-
- @Test
- fun pickerScreenState_whenGlanceableHubDisabled_returnsDisabled() =
- kosmos.runTest {
- setCommunalV2Enabled(false)
- runCurrent()
-
- assertThat(
- underTest.getPickerScreenState()
- is KeyguardQuickAffordanceConfig.PickerScreenState.Disabled
- )
- }
-
- @Test
- @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
- fun onTriggered_changesSceneToCommunal() =
- kosmos.runTest {
- underTest.onTriggered(expandable = null)
- runCurrent()
-
- assertThat(kosmos.communalSceneRepository.currentScene.value)
- .isEqualTo(CommunalScenes.Communal)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_SCENE_CONTAINER)
- fun testTransitionToGlanceableHub_sceneContainer() =
- kosmos.runTest {
- underTest.onTriggered(expandable = null)
- runCurrent()
-
- assertThat(kosmos.sceneContainerRepository.currentScene.value)
- .isEqualTo(Scenes.Communal)
- }
-
- companion object {
- @JvmStatic
- @Parameters(name = "{0}")
- fun getParams(): List<FlagsParameterization> {
- return parameterizeSceneContainerFlag()
- }
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelTest.kt
new file mode 100644
index 000000000000..38829da69c28
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.media.controls.data.repository.mediaFilterRepository
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyguardMediaViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+
+ private val underTest = kosmos.keyguardMediaViewModelFactory.create()
+
+ @Before
+ fun setUp() {
+ underTest.activateIn(kosmos.testScope)
+ }
+
+ @Test
+ fun onDozing_noActiveMedia_mediaIsHidden() =
+ kosmos.runTest {
+ keyguardRepository.setIsDozing(true)
+
+ assertThat(underTest.isMediaVisible).isFalse()
+ }
+
+ @Test
+ fun onDozing_activeMediaExists_mediaIsHidden() =
+ kosmos.runTest {
+ val userMedia = MediaData(active = true)
+
+ mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ keyguardRepository.setIsDozing(true)
+
+ assertThat(underTest.isMediaVisible).isFalse()
+ }
+
+ @Test
+ fun onDeviceAwake_activeMediaExists_mediaIsVisible() =
+ kosmos.runTest {
+ val userMedia = MediaData(active = true)
+
+ mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ keyguardRepository.setIsDozing(false)
+
+ assertThat(underTest.isMediaVisible).isTrue()
+ }
+
+ @Test
+ fun onDeviceAwake_noActiveMedia_mediaIsHidden() =
+ kosmos.runTest {
+ keyguardRepository.setIsDozing(false)
+
+ assertThat(underTest.isMediaVisible).isFalse()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
index 555ba56e087f..7f9313cbeb5b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.navigationbar.views;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -30,6 +31,8 @@ import static com.android.systemui.assist.AssistManager.INVOCATION_TYPE_HOME_BUT
import static com.android.systemui.navigationbar.views.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
import static com.android.systemui.navigationbar.views.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_LONGPRESS;
import static com.android.systemui.navigationbar.views.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static com.google.common.truth.Truth.assertThat;
@@ -487,6 +490,65 @@ public class NavigationBarTest extends SysuiTestCase {
verify(mUserTracker).addCallback(any(UserTracker.Callback.class), any(Executor.class));
}
+ /**
+ * Verifies that the SysUI state is updated correctly when given a new IME window status with
+ * IME visible and IME Switcher button visible.
+ */
+ @Test
+ public void testSetImeWindowStatusSysuiState_ImeVisibleImeSwitcherButtonVisible() {
+ doNothing().when(mNavigationBar).checkNavBarModes();
+
+ mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
+ BACK_DISPOSITION_DEFAULT, true /* showImeSwitcher */);
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(true));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_SHOWING), eq(true));
+ }
+
+ /**
+ * Verifies that the SysUI state is updated correctly when given a new IME window status with
+ * IME visible and IME Switcher button not visible.
+ */
+ @Test
+ public void testSetImeWindowStatusSysuiState_ImeVisibleImeSwitcherButtonNotVisible() {
+ doNothing().when(mNavigationBar).checkNavBarModes();
+
+ mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
+ BACK_DISPOSITION_DEFAULT, false /* showImeSwitcher */);
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(true));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_SHOWING), eq(false));
+ }
+
+ /**
+ * Verifies that the SysUI state is updated correctly when given a new IME window status with
+ * IME not visible and IME Switcher button visible.
+ */
+ @Test
+ public void testSetImeWindowStatusSysuiState_ImeNotVisibleImeSwitcherButtonVisible() {
+ doNothing().when(mNavigationBar).checkNavBarModes();
+ // Set initial state for later reset to be able to take place.
+ mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
+ BACK_DISPOSITION_DEFAULT, true /* showImeSwitcher */);
+
+ mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, 0 /* vis */,
+ BACK_DISPOSITION_DEFAULT, true /* showImeSwitcher */);
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(false));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_SHOWING), eq(false));
+ }
+
+ /**
+ * Verifies that the SysUI state is updated correctly when given a new IME window status with
+ * IME visible and back disposition adjust nothing.
+ */
+ @Test
+ public void testSetImeWindowStatusSysuiState_ImeVisibleBackDispositionAdjustNothing() {
+ doNothing().when(mNavigationBar).checkNavBarModes();
+
+ mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
+ BACK_DISPOSITION_ADJUST_NOTHING, true /* showImeSwitcher */);
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(true));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_SHOWING), eq(true));
+ }
+
@Test
public void testSetImeWindowStatusWhenImeSwitchOnDisplay() {
// Create default & external NavBar fragment.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModelTest.kt
deleted file mode 100644
index 46b02e92a4f9..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModelTest.kt
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.notifications.ui.viewmodel
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
-import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.ui.viewmodel.notificationsShadeUserActionsViewModel
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
-@EnableSceneContainer
-class NotificationsShadeUserActionsViewModelTest : SysuiTestCase() {
-
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
- private val sceneInteractor by lazy { kosmos.sceneInteractor }
- private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
-
- private val underTest by lazy { kosmos.notificationsShadeUserActionsViewModel }
-
- @Test
- fun upTransitionSceneKey_deviceLocked_lockscreen() =
- testScope.runTest {
- val actions by collectLastValue(underTest.actions)
- lockDevice()
- underTest.activateIn(this)
-
- assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(actions?.get(Swipe.Down)).isNull()
- assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
- .isEqualTo(Scenes.Lockscreen)
- }
-
- @Test
- fun upTransitionSceneKey_deviceLocked_keyguardDisabled_gone() =
- testScope.runTest {
- val actions by collectLastValue(underTest.actions)
- lockDevice()
- kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
- underTest.activateIn(this)
-
- assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value).isEqualTo(Scenes.Gone)
- }
-
- @Test
- fun upTransitionSceneKey_deviceUnlocked_gone() =
- testScope.runTest {
- val actions by collectLastValue(underTest.actions)
- lockDevice()
- unlockDevice()
- underTest.activateIn(this)
-
- assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(actions?.get(Swipe.Down)).isNull()
- assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
- }
-
- @Test
- fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
- testScope.runTest {
- val actions by collectLastValue(underTest.actions)
- kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.None
- )
- sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
- underTest.activateIn(this)
-
- assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
- .isEqualTo(Scenes.Lockscreen)
- }
-
- @Test
- fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
- testScope.runTest {
- val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
- val actions by collectLastValue(underTest.actions)
- kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.None
- )
- assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
- sceneInteractor // force the lazy; this will kick off StateFlows
- runCurrent()
- sceneInteractor.changeScene(Scenes.Gone, "reason")
- underTest.activateIn(this)
-
- assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value).isEqualTo(Scenes.Gone)
- }
-
- private fun TestScope.lockDevice() {
- val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
- sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
- runCurrent()
- }
-
- private fun TestScope.unlockDevice() {
- val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
- kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
- SuccessFingerprintAuthenticationStatus(0, true)
- )
- assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
- sceneInteractor.changeScene(Scenes.Gone, "reason")
- runCurrent()
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
index 3d014b6822b4..a82a7de75cc0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
@@ -34,7 +34,7 @@ import com.android.systemui.scene.data.repository.WindowRootViewVisibilityReposi
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
-import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.init.NotificationsController
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
@@ -70,8 +70,7 @@ class WindowRootViewVisibilityInteractorTest : SysuiTestCase() {
private val notificationsController = mock<NotificationsController>()
private val powerInteractor = PowerInteractorFactory.create().powerInteractor
private val activeNotificationsRepository = kosmos.activeNotificationListRepository
- private val activeNotificationsInteractor =
- ActiveNotificationsInteractor(activeNotificationsRepository, testDispatcher)
+ private val activeNotificationsInteractor = kosmos.activeNotificationsInteractor
private val underTest =
WindowRootViewVisibilityInteractor(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
index 27e9f07af168..3d5daf6cf9c2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -318,7 +318,7 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
val displayId = 1
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = displayId))
val onSaved = { _: Uri? -> }
- focusedDisplayRepository.emit(displayId)
+ focusedDisplayRepository.setDisplayId(displayId)
screenshotExecutor.executeScreenshots(
createScreenshotRequest(
@@ -345,7 +345,7 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
display(TYPE_INTERNAL, id = Display.DEFAULT_DISPLAY),
display(TYPE_EXTERNAL, id = 1),
)
- focusedDisplayRepository.emit(5) // invalid display
+ focusedDisplayRepository.setDisplayId(5) // invalid display
val onSaved = { _: Uri? -> }
screenshotExecutor.executeScreenshots(
createScreenshotRequest(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
index ab5fa8ef43fb..5566c10dc9e9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
@@ -38,7 +38,7 @@ class QsBatteryModeControllerTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val insetsProviderStore = kosmos.fakeStatusBarContentInsetsProviderStore
- private val insetsProvider = insetsProviderStore.defaultDisplay
+ private val insetsProvider = insetsProviderStore.forDisplay(context.displayId)
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()!!
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
index 007a0fb87953..4f332d4bbed8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
@@ -17,15 +17,21 @@
package com.android.systemui.shade.data.repository
import android.provider.Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS
+import android.view.Display
+import android.view.Display.TYPE_EXTERNAL
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.display.data.repository.display
import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.shade.display.AnyExternalShadeDisplayPolicy
import com.android.systemui.shade.display.DefaultDisplayShadePolicy
+import com.android.systemui.shade.display.FakeShadeDisplayPolicy
import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeGlobalSettings
@@ -37,24 +43,28 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class ShadeDisplaysRepositoryTest : SysuiTestCase() {
- private val kosmos = testKosmos()
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testScope = kosmos.testScope
private val globalSettings = kosmos.fakeGlobalSettings
private val displayRepository = kosmos.displayRepository
private val defaultPolicy = DefaultDisplayShadePolicy()
private val policies = kosmos.shadeDisplayPolicies
+ private val keyguardRepository = kosmos.fakeKeyguardRepository
- private val underTest =
+ private fun createUnderTest(shadeOnDefaultDisplayWhenLocked: Boolean = false) =
ShadeDisplaysRepositoryImpl(
globalSettings,
defaultPolicy,
testScope.backgroundScope,
policies,
+ shadeOnDefaultDisplayWhenLocked = shadeOnDefaultDisplayWhenLocked,
+ keyguardRepository,
)
@Test
fun policy_changing_propagatedFromTheLatestPolicy() =
testScope.runTest {
+ val underTest = createUnderTest()
val displayIds by collectValues(underTest.displayId)
assertThat(displayIds).containsExactly(0)
@@ -81,30 +91,54 @@ class ShadeDisplaysRepositoryTest : SysuiTestCase() {
@Test
fun policy_updatesBasedOnSettingValue_defaultDisplay() =
testScope.runTest {
- val policy by collectLastValue(underTest.policy)
-
+ val underTest = createUnderTest()
globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "default_display")
- assertThat(policy).isInstanceOf(DefaultDisplayShadePolicy::class.java)
+ assertThat(underTest.currentPolicy).isInstanceOf(DefaultDisplayShadePolicy::class.java)
}
@Test
fun policy_updatesBasedOnSettingValue_anyExternal() =
testScope.runTest {
- val policy by collectLastValue(underTest.policy)
-
+ val underTest = createUnderTest()
globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "any_external_display")
- assertThat(policy).isInstanceOf(AnyExternalShadeDisplayPolicy::class.java)
+ assertThat(underTest.currentPolicy)
+ .isInstanceOf(AnyExternalShadeDisplayPolicy::class.java)
}
@Test
fun policy_updatesBasedOnSettingValue_focusBased() =
testScope.runTest {
- val policy by collectLastValue(underTest.policy)
-
+ val underTest = createUnderTest()
globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "status_bar_latest_touch")
- assertThat(policy).isInstanceOf(StatusBarTouchShadeDisplayPolicy::class.java)
+ assertThat(underTest.currentPolicy)
+ .isInstanceOf(StatusBarTouchShadeDisplayPolicy::class.java)
+ }
+
+ @Test
+ fun displayId_afterKeyguardHides_goesBackToPreviousDisplay() =
+ testScope.runTest {
+ val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
+ globalSettings.putString(
+ DEVELOPMENT_SHADE_DISPLAY_AWARENESS,
+ FakeShadeDisplayPolicy.name,
+ )
+
+ val displayId by collectLastValue(underTest.displayId)
+
+ displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
+ FakeShadeDisplayPolicy.setDisplayId(2)
+
+ assertThat(displayId).isEqualTo(2)
+
+ keyguardRepository.setKeyguardShowing(true)
+
+ assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
+
+ keyguardRepository.setKeyguardShowing(false)
+
+ assertThat(displayId).isEqualTo(2)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt
index eeb3e6b31c69..fd6bc98b006c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt
@@ -25,7 +25,7 @@ import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.shade.ShadePrimaryDisplayCommand
-import com.android.systemui.shade.display.ShadeDisplayPolicy
+import com.android.systemui.shade.display.FakeShadeDisplayPolicy
import com.android.systemui.statusbar.commandline.commandRegistry
import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeGlobalSettings
@@ -33,8 +33,6 @@ import com.google.common.truth.StringSubject
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -118,23 +116,12 @@ class ShadePrimaryDisplayCommandTest : SysuiTestCase() {
@Test
fun policies_setsNewPolicy() =
testScope.runTest {
- val policy by collectLastValue(shadeDisplaysRepository.policy)
- val newPolicy = policies.last().name
+ val newPolicy = FakeShadeDisplayPolicy.name
commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", newPolicy))
- assertThat(policy!!.name).isEqualTo(newPolicy)
+ assertThat(shadeDisplaysRepository.currentPolicy.name).isEqualTo(newPolicy)
}
-
- private fun makePolicy(policyName: String): ShadeDisplayPolicy {
- return object : ShadeDisplayPolicy {
- override val name: String
- get() = policyName
-
- override val displayId: StateFlow<Int>
- get() = MutableStateFlow(0)
- }
- }
}
private fun StringSubject.containsAllIn(strings: List<String>) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/FocusShadeDisplayPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/FocusShadeDisplayPolicyTest.kt
new file mode 100644
index 000000000000..b4249ef72e62
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/FocusShadeDisplayPolicyTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.display
+
+import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.shade.data.repository.fakeFocusedDisplayRepository
+import com.android.systemui.shade.data.repository.focusShadeDisplayPolicy
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FocusShadeDisplayPolicyTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val testScope = kosmos.testScope
+ private val focusedDisplayRepository = kosmos.fakeFocusedDisplayRepository
+
+ private val underTest = kosmos.focusShadeDisplayPolicy
+
+ @Test
+ fun displayId_propagatedFromRepository() =
+ testScope.runTest {
+ val displayId by collectLastValue(underTest.displayId)
+
+ assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
+
+ focusedDisplayRepository.setDisplayId(2)
+
+ assertThat(displayId).isEqualTo(2)
+
+ focusedDisplayRepository.setDisplayId(3)
+
+ assertThat(displayId).isEqualTo(3)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
index 20dfd3e11947..e43c46b36a06 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
@@ -26,12 +26,11 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.display.data.repository.display
import com.android.systemui.display.data.repository.displayRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.shade.data.repository.statusBarTouchShadeDisplayPolicy
import com.android.systemui.shade.domain.interactor.notificationElement
import com.android.systemui.shade.domain.interactor.qsElement
-import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
@@ -45,22 +44,9 @@ import org.mockito.kotlin.mock
class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testScope = kosmos.testScope
- private val keyguardRepository = kosmos.fakeKeyguardRepository
private val displayRepository = kosmos.displayRepository
- private fun createUnderTest(
- shadeOnDefaultDisplayWhenLocked: Boolean = false
- ): StatusBarTouchShadeDisplayPolicy {
- return StatusBarTouchShadeDisplayPolicy(
- displayRepository,
- keyguardRepository,
- testScope.backgroundScope,
- shadeOnDefaultDisplayWhenLocked = shadeOnDefaultDisplayWhenLocked,
- shadeInteractor = { kosmos.shadeInteractor },
- { kosmos.qsElement },
- { kosmos.notificationElement },
- )
- }
+ private val underTest = kosmos.statusBarTouchShadeDisplayPolicy
private fun createMotionEventForDisplay(displayId: Int, xCoordinate: Float = 0f): MotionEvent {
return mock<MotionEvent> {
@@ -71,15 +57,12 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
@Test
fun displayId_defaultToDefaultDisplay() {
- val underTest = createUnderTest()
-
assertThat(underTest.displayId.value).isEqualTo(Display.DEFAULT_DISPLAY)
}
@Test
fun onStatusBarTouched_called_updatesDisplayId() =
testScope.runTest {
- val underTest = createUnderTest()
val displayId by collectLastValue(underTest.displayId)
displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
@@ -91,7 +74,6 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
@Test
fun onStatusBarTouched_notExistentDisplay_displayIdNotUpdated() =
testScope.runTest {
- val underTest = createUnderTest()
val displayIds by collectValues(underTest.displayId)
assertThat(displayIds).isEqualTo(listOf(Display.DEFAULT_DISPLAY))
@@ -104,7 +86,6 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
@Test
fun onStatusBarTouched_afterDisplayRemoved_goesBackToDefaultDisplay() =
testScope.runTest {
- val underTest = createUnderTest()
val displayId by collectLastValue(underTest.displayId)
displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
@@ -118,46 +99,8 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
}
@Test
- fun onStatusBarTouched_afterKeyguardVisible_goesBackToDefaultDisplay() =
- testScope.runTest {
- val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
- val displayId by collectLastValue(underTest.displayId)
-
- displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
- underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
-
- assertThat(displayId).isEqualTo(2)
-
- keyguardRepository.setKeyguardShowing(true)
-
- assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
- }
-
- @Test
- fun onStatusBarTouched_afterKeyguardHides_goesBackToPreviousDisplay() =
- testScope.runTest {
- val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
- val displayId by collectLastValue(underTest.displayId)
-
- displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
- underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
-
- assertThat(displayId).isEqualTo(2)
-
- keyguardRepository.setKeyguardShowing(true)
-
- assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
-
- keyguardRepository.setKeyguardShowing(false)
-
- assertThat(displayId).isEqualTo(2)
- }
-
- @Test
fun onStatusBarTouched_leftSide_intentSetToNotifications() =
testScope.runTest {
- val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
-
underTest.onStatusBarTouched(
createMotionEventForDisplay(2, STATUS_BAR_WIDTH * 0.1f),
STATUS_BAR_WIDTH,
@@ -169,8 +112,6 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
@Test
fun onStatusBarTouched_rightSide_intentSetToQs() =
testScope.runTest {
- val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
-
underTest.onStatusBarTouched(
createMotionEventForDisplay(2, STATUS_BAR_WIDTH * 0.95f),
STATUS_BAR_WIDTH,
@@ -182,8 +123,6 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
@Test
fun onStatusBarTouched_nullAfterConsumed() =
testScope.runTest {
- val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
-
underTest.onStatusBarTouched(
createMotionEventForDisplay(2, STATUS_BAR_WIDTH * 0.1f),
STATUS_BAR_WIDTH,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt
index d3ba3dceb4cf..246283c236fe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt
@@ -22,17 +22,24 @@ import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.configurationRepository
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.scene.ui.view.mockShadeRootView
import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
+import com.android.systemui.statusbar.notification.row.notificationRebindingTracker
+import com.android.systemui.statusbar.notification.stack.notificationStackRebindingHider
import com.android.systemui.testKosmos
-import kotlinx.coroutines.test.advanceUntilIdle
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
@@ -42,7 +49,7 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
@SmallTest
class ShadeDisplaysInteractorTest : SysuiTestCase() {
- val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testScope = kosmos.testScope
private val shadeRootview = kosmos.mockShadeRootView
@@ -52,6 +59,10 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {
private val latencyTracker = kosmos.mockedShadeDisplayChangeLatencyTracker
private val configuration = mock<Configuration>()
private val display = mock<Display>()
+ private val activeNotificationRepository = kosmos.activeNotificationListRepository
+ private val notificationRebindingTracker = kosmos.notificationRebindingTracker
+ private val notificationStackRebindingHider = kosmos.notificationStackRebindingHider
+ private val configurationRepository = kosmos.fakeConfigurationRepository
private val underTest by lazy { kosmos.shadeDisplaysInteractor }
@@ -68,24 +79,26 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {
}
@Test
- fun start_shadeInCorrectPosition_notAddedOrRemoved() {
- whenever(display.displayId).thenReturn(0)
- positionRepository.setDisplayId(0)
+ fun start_shadeInCorrectPosition_notAddedOrRemoved() =
+ testScope.runTest {
+ whenever(display.displayId).thenReturn(0)
+ positionRepository.setDisplayId(0)
- underTest.start()
+ underTest.start()
- verify(shadeContext, never()).reparentToDisplay(any())
- }
+ verify(shadeContext, never()).reparentToDisplay(any())
+ }
@Test
- fun start_shadeInWrongPosition_changes() {
- whenever(display.displayId).thenReturn(0)
- positionRepository.setDisplayId(1)
+ fun start_shadeInWrongPosition_changes() =
+ testScope.runTest {
+ whenever(display.displayId).thenReturn(0)
+ positionRepository.setDisplayId(1)
- underTest.start()
+ underTest.start()
- verify(shadeContext).reparentToDisplay(eq(1))
- }
+ verify(shadeContext).reparentToDisplay(eq(1))
+ }
@Test
fun start_shadeInWrongPosition_logsStartToLatencyTracker() =
@@ -94,8 +107,81 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {
positionRepository.setDisplayId(1)
underTest.start()
- advanceUntilIdle()
verify(latencyTracker).onShadeDisplayChanging(eq(1))
}
+
+ @Test
+ fun start_shadeInWrongPosition_someNotificationsVisible_hiddenThenShown() =
+ testScope.runTest {
+ whenever(display.displayId).thenReturn(0)
+ positionRepository.setDisplayId(1)
+ activeNotificationRepository.setActiveNotifs(1)
+
+ underTest.start()
+
+ verify(notificationStackRebindingHider).setVisible(eq(false), eq(false))
+ configurationRepository.onMovedToDisplay(1)
+ verify(notificationStackRebindingHider).setVisible(eq(true), eq(true))
+ }
+
+ @Test
+ fun start_shadeInWrongPosition_someNotificationsVisible_waitsForInflationsBeforeShowingNssl() =
+ testScope.runTest {
+ whenever(display.displayId).thenReturn(0)
+ positionRepository.setDisplayId(1)
+ activeNotificationRepository.setActiveNotifs(1)
+
+ val endRebinding = notificationRebindingTracker.trackRebinding("test")
+
+ assertThat(notificationRebindingTracker.rebindingInProgressCount.value).isEqualTo(1)
+
+ underTest.start()
+
+ verify(notificationStackRebindingHider).setVisible(eq(false), eq(false))
+ configurationRepository.onMovedToDisplay(1)
+
+ // Verify that setVisible(true, true) is NOT called yet, as we
+ // first need to wait for notification bindings to have happened
+ verify(notificationStackRebindingHider, never()).setVisible(eq(true), eq(true))
+
+ endRebinding.onFinished()
+
+ // Now verify that setVisible(true, true) is called
+ verify(notificationStackRebindingHider, times(1)).setVisible(eq(true), eq(true))
+ }
+
+ @Test
+ fun start_shadeInWrongPosition_noNotifications_nsslNotHidden() =
+ testScope.runTest {
+ whenever(display.displayId).thenReturn(0)
+ positionRepository.setDisplayId(1)
+ activeNotificationRepository.setActiveNotifs(0)
+
+ underTest.start()
+
+ verify(notificationStackRebindingHider)
+ .setVisible(visible = eq(true), animated = eq(false))
+ verify(notificationStackRebindingHider, never()).setVisible(eq(false), eq(false))
+ verify(notificationStackRebindingHider, never()).setVisible(eq(true), eq(true))
+ }
+
+ @Test
+ fun start_shadeInWrongPosition_waitsUntilMovedToDisplayReceived() =
+ testScope.runTest {
+ whenever(display.displayId).thenReturn(0)
+ positionRepository.setDisplayId(1)
+ activeNotificationRepository.setActiveNotifs(1)
+
+ underTest.start()
+
+ verify(notificationStackRebindingHider).setVisible(eq(false), eq(false))
+ // It's not set to visible yet, as we first need to wait for the view to receive the
+ // display moved callback.
+ verify(notificationStackRebindingHider, never()).setVisible(eq(true), eq(true))
+
+ configurationRepository.onMovedToDisplay(1)
+
+ verify(notificationStackRebindingHider).setVisible(eq(true), eq(true))
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
index a47db2ec728b..668f568d7f46 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorImplTest.kt
@@ -16,15 +16,11 @@
package com.android.systemui.shade.domain.interactor
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
-import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -48,82 +44,79 @@ class ShadeModeInteractorImplTest : SysuiTestCase() {
}
@Test
- @DisableFlags(DualShade.FLAG_NAME)
fun legacyShadeMode_narrowScreen_singleShade() =
testScope.runTest {
val shadeMode by collectLastValue(underTest.shadeMode)
- kosmos.shadeRepository.setShadeLayoutWide(false)
+ kosmos.enableSingleShade()
assertThat(shadeMode).isEqualTo(ShadeMode.Single)
}
@Test
- @DisableFlags(DualShade.FLAG_NAME)
fun legacyShadeMode_wideScreen_splitShade() =
testScope.runTest {
val shadeMode by collectLastValue(underTest.shadeMode)
- kosmos.shadeRepository.setShadeLayoutWide(true)
+ kosmos.enableSplitShade()
assertThat(shadeMode).isEqualTo(ShadeMode.Split)
}
@Test
- @EnableFlags(DualShade.FLAG_NAME)
fun shadeMode_wideScreen_isDual() =
testScope.runTest {
val shadeMode by collectLastValue(underTest.shadeMode)
- kosmos.shadeRepository.setShadeLayoutWide(true)
+ kosmos.enableDualShade(wideLayout = true)
assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
}
@Test
- @EnableFlags(DualShade.FLAG_NAME)
fun shadeMode_narrowScreen_isDual() =
testScope.runTest {
val shadeMode by collectLastValue(underTest.shadeMode)
- kosmos.shadeRepository.setShadeLayoutWide(false)
+ kosmos.enableDualShade(wideLayout = false)
assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
}
@Test
- @EnableFlags(DualShade.FLAG_NAME)
- fun isDualShade_flagEnabled_true() =
+ fun isDualShade_settingEnabled_returnsTrue() =
testScope.runTest {
- // Initiate collection.
+ // TODO(b/391578667): Add a test case for user switching once the bug is fixed.
val shadeMode by collectLastValue(underTest.shadeMode)
+ kosmos.enableDualShade()
+ assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
assertThat(underTest.isDualShade).isTrue()
}
@Test
- @DisableFlags(DualShade.FLAG_NAME)
- fun isDualShade_flagDisabled_false() =
+ fun isDualShade_settingDisabled_returnsFalse() =
testScope.runTest {
- // Initiate collection.
val shadeMode by collectLastValue(underTest.shadeMode)
+ kosmos.disableDualShade()
+ assertThat(shadeMode).isNotEqualTo(ShadeMode.Dual)
assertThat(underTest.isDualShade).isFalse()
}
@Test
fun getTopEdgeSplitFraction_narrowScreen_splitInHalf() =
testScope.runTest {
- // Ensure isShadeLayoutWide is collected.
- val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide)
- kosmos.shadeRepository.setShadeLayoutWide(false)
+ val shadeMode by collectLastValue(underTest.shadeMode)
+ kosmos.enableDualShade(wideLayout = false)
+ assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
assertThat(underTest.getTopEdgeSplitFraction()).isEqualTo(0.5f)
}
@Test
fun getTopEdgeSplitFraction_wideScreen_splitInHalf() =
testScope.runTest {
- // Ensure isShadeLayoutWide is collected.
- val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide)
- kosmos.shadeRepository.setShadeLayoutWide(true)
+ val shadeMode by collectLastValue(underTest.shadeMode)
+ kosmos.enableDualShade(wideLayout = true)
+ assertThat(shadeMode).isEqualTo(ShadeMode.Dual)
assertThat(underTest.getTopEdgeSplitFraction()).isEqualTo(0.5f)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index eb8ea8ba15cf..0da1e7f11582 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -102,12 +102,12 @@ class ShadeHeaderViewModelTest : SysuiTestCase() {
@Test
@DisableFlags(DualShade.FLAG_NAME)
- fun onSystemIconContainerClicked_locked_collapsesShadeToLockscreen() =
+ fun onSystemIconChipClicked_locked_collapsesShadeToLockscreen() =
testScope.runTest {
setDeviceEntered(false)
setScene(Scenes.Shade)
- underTest.onSystemIconContainerClicked()
+ underTest.onSystemIconChipClicked()
runCurrent()
assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen)
@@ -115,16 +115,16 @@ class ShadeHeaderViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(DualShade.FLAG_NAME)
- fun onSystemIconContainerClicked_lockedOnDualShade_collapsesShadeToLockscreen() =
+ fun onSystemIconChipClicked_lockedOnQsShade_collapsesShadeToLockscreen() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene)
val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
setDeviceEntered(false)
setScene(Scenes.Lockscreen)
- setOverlay(Overlays.NotificationsShade)
+ setOverlay(Overlays.QuickSettingsShade)
assertThat(currentOverlays).isNotEmpty()
- underTest.onSystemIconContainerClicked()
+ underTest.onSystemIconChipClicked()
runCurrent()
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
@@ -132,13 +132,32 @@ class ShadeHeaderViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun onSystemIconChipClicked_lockedOnNotifShade_expandsQsShade() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ setDeviceEntered(false)
+ setScene(Scenes.Lockscreen)
+ setOverlay(Overlays.NotificationsShade)
+ assertThat(currentOverlays).isNotEmpty()
+
+ underTest.onSystemIconChipClicked()
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).contains(Overlays.QuickSettingsShade)
+ assertThat(currentOverlays).doesNotContain(Overlays.NotificationsShade)
+ }
+
+ @Test
@DisableFlags(DualShade.FLAG_NAME)
- fun onSystemIconContainerClicked_unlocked_collapsesShadeToGone() =
+ fun onSystemIconChipClicked_unlocked_collapsesShadeToGone() =
testScope.runTest {
setDeviceEntered(true)
setScene(Scenes.Shade)
- underTest.onSystemIconContainerClicked()
+ underTest.onSystemIconChipClicked()
runCurrent()
assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
@@ -146,7 +165,81 @@ class ShadeHeaderViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(DualShade.FLAG_NAME)
- fun onSystemIconContainerClicked_unlockedOnDualShade_collapsesShadeToGone() =
+ fun onSystemIconChipClicked_unlockedOnQsShade_collapsesShadeToGone() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ setDeviceEntered(true)
+ setScene(Scenes.Gone)
+ setOverlay(Overlays.QuickSettingsShade)
+ assertThat(currentOverlays).isNotEmpty()
+
+ underTest.onSystemIconChipClicked()
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ assertThat(currentOverlays).isEmpty()
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun onSystemIconChipClicked_unlockedOnNotifShade_expandsQsShade() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ setDeviceEntered(true)
+ setScene(Scenes.Gone)
+ setOverlay(Overlays.NotificationsShade)
+ assertThat(currentOverlays).isNotEmpty()
+
+ underTest.onSystemIconChipClicked()
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ assertThat(currentOverlays).contains(Overlays.QuickSettingsShade)
+ assertThat(currentOverlays).doesNotContain(Overlays.NotificationsShade)
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun onNotificationIconChipClicked_lockedOnNotifShade_collapsesShadeToLockscreen() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ setDeviceEntered(false)
+ setScene(Scenes.Lockscreen)
+ setOverlay(Overlays.NotificationsShade)
+ assertThat(currentOverlays).isNotEmpty()
+
+ underTest.onNotificationIconChipClicked()
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).isEmpty()
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun onNotificationIconChipClicked_lockedOnQsShade_expandsNotifShade() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ setDeviceEntered(false)
+ setScene(Scenes.Lockscreen)
+ setOverlay(Overlays.QuickSettingsShade)
+ assertThat(currentOverlays).isNotEmpty()
+
+ underTest.onNotificationIconChipClicked()
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).contains(Overlays.NotificationsShade)
+ assertThat(currentOverlays).doesNotContain(Overlays.QuickSettingsShade)
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun onNotificationIconChipClicked_unlockedOnNotifShade_collapsesShadeToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene)
val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
@@ -155,13 +248,32 @@ class ShadeHeaderViewModelTest : SysuiTestCase() {
setOverlay(Overlays.NotificationsShade)
assertThat(currentOverlays).isNotEmpty()
- underTest.onSystemIconContainerClicked()
+ underTest.onNotificationIconChipClicked()
runCurrent()
assertThat(currentScene).isEqualTo(Scenes.Gone)
assertThat(currentOverlays).isEmpty()
}
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun onNotificationIconChipClicked_unlockedOnQsShade_expandsNotifShade() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ setDeviceEntered(true)
+ setScene(Scenes.Gone)
+ setOverlay(Overlays.QuickSettingsShade)
+ assertThat(currentOverlays).isNotEmpty()
+
+ underTest.onNotificationIconChipClicked()
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ assertThat(currentOverlays).contains(Overlays.NotificationsShade)
+ assertThat(currentOverlays).doesNotContain(Overlays.QuickSettingsShade)
+ }
+
companion object {
private val SUB_1 =
SubscriptionModel(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerStateTest.kt
index e68045fe470f..4e92540396d3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerStateTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.ui.compose
+package com.android.systemui.statusbar.chips.ui.viewmodel
import android.text.format.DateUtils.formatElapsedTime
import androidx.test.ext.junit.runners.AndroidJUnit4
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index 8bca17f72c9f..25ae13fefdd7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -109,6 +109,7 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
private val statusBarService: IStatusBarService = mock()
private val uiEventLogger: UiEventLogger = mock()
private val msdlPlayer: MSDLPlayer = mock()
+ private val rebindingTracker: NotificationRebindingTracker = mock()
private lateinit var controller: ExpandableNotificationRowController
@Before
@@ -150,6 +151,7 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
statusBarService,
uiEventLogger,
msdlPlayer,
+ rebindingTracker,
)
whenever(view.childrenContainer).thenReturn(childrenContainer)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRebindingTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRebindingTrackerTest.kt
new file mode 100644
index 000000000000..8bffab617e0a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRebindingTrackerTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.row
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationRebindingTrackerTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val testScope = kosmos.testScope
+ private val activeNotificationRepository = kosmos.activeNotificationListRepository
+
+ private val underTest: NotificationRebindingTracker = kosmos.notificationRebindingTracker
+
+ @Before
+ fun setup() {
+ underTest.start()
+ }
+
+ @Test
+ fun rebindingInProgressCount_noneStarted_isZero() =
+ testScope.runTest {
+ val count by collectLastValue(underTest.rebindingInProgressCount)
+
+ assertThat(count).isEqualTo(0)
+ }
+
+ @Test
+ fun rebindingInProgressCount_oneStarted_isOne() =
+ testScope.runTest {
+ val count by collectLastValue(underTest.rebindingInProgressCount)
+ activeNotificationRepository.setActiveNotifs(1)
+
+ underTest.trackRebinding("0")
+
+ assertThat(count).isEqualTo(1)
+ }
+
+ @Test
+ fun rebindingInProgressCount_oneStartedThenFinished_goesFromOneToZero() =
+ testScope.runTest {
+ val count by collectLastValue(underTest.rebindingInProgressCount)
+ activeNotificationRepository.setActiveNotifs(1)
+
+ val endRebinding = underTest.trackRebinding("0")
+
+ assertThat(count).isEqualTo(1)
+
+ endRebinding.onFinished()
+
+ assertThat(count).isEqualTo(0)
+ }
+
+ @Test
+ fun rebindingInProgressCount_twoStarted_goesToTwo() =
+ testScope.runTest {
+ val count by collectLastValue(underTest.rebindingInProgressCount)
+ activeNotificationRepository.setActiveNotifs(2)
+
+ underTest.trackRebinding("0")
+ underTest.trackRebinding("1")
+
+ assertThat(count).isEqualTo(2)
+ }
+
+ @Test
+ fun rebindingInProgressCount_twoStarted_oneNotActiveAnymore_goesToZero() =
+ testScope.runTest {
+ val count by collectLastValue(underTest.rebindingInProgressCount)
+ activeNotificationRepository.setActiveNotifs(2)
+
+ val finishFirstRebinding = underTest.trackRebinding("0")
+ underTest.trackRebinding("1")
+
+ assertThat(count).isEqualTo(2)
+
+ activeNotificationRepository.setActiveNotifs(1)
+
+ assertThat(count).isEqualTo(1)
+
+ finishFirstRebinding.onFinished()
+
+ assertThat(count).isEqualTo(0)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index b8d18757afbb..c39b252cd795 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -653,7 +653,8 @@ public class NotificationTestHelper {
mock(SmartReplyConstants.class),
mock(SmartReplyController.class),
mock(IStatusBarService.class),
- mock(UiEventLogger.class));
+ mock(UiEventLogger.class),
+ mock(NotificationRebindingTracker.class));
row.setAboveShelfChangedListener(aboveShelf -> { });
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index 50db9f7268e4..4b8a0c21f03d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.stack
import android.annotation.DimenRes
+import android.platform.test.annotations.EnableFlags
import android.service.notification.StatusBarNotification
import android.view.View.VISIBLE
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -30,6 +31,7 @@ import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
@@ -152,6 +154,29 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+ fun maxKeyguardNotificationsForPromotedOngoing_onLockscreenSpaceForMinHeightButNotIntrinsicHeight_returnsOne() {
+ setGapHeight(0f)
+ // No divider height since we're testing one element where index = 0
+
+ whenever(sysuiStatusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(lockscreenShadeTransitionController.fractionToShade).thenReturn(0f)
+
+ val row = createMockRow(10f, isPromotedOngoing = true)
+ whenever(row.getMinHeight(any())).thenReturn(5)
+
+ val maxNotifications =
+ computeMaxKeyguardNotifications(
+ listOf(row),
+ /* spaceForNotifications= */ 5f,
+ /* spaceForShelf= */ 0f,
+ /* shelfHeight= */ 0f,
+ )
+
+ assertThat(maxNotifications).isEqualTo(1)
+ }
+
+ @Test
fun computeMaxKeyguardNotifications_spaceForTwo_returnsTwo() {
setGapHeight(gapHeight)
val shelfHeight = shelfHeight + dividerHeight
@@ -257,6 +282,26 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+ fun getSpaceNeeded_onLockscreenEnoughSpacePromotedOngoing_intrinsicHeight() {
+ setGapHeight(0f)
+ // No divider height since we're testing one element where index = 0
+
+ val row = createMockRow(10f, isPromotedOngoing = true)
+ whenever(row.getMinHeight(any())).thenReturn(5)
+
+ val space =
+ sizeCalculator.getSpaceNeeded(
+ row,
+ visibleIndex = 0,
+ previousView = null,
+ stack = stackLayout,
+ onLockscreen = true,
+ )
+ assertThat(space.whenEnoughSpace).isEqualTo(10f)
+ }
+
+ @Test
fun getSpaceNeeded_onLockscreenEnoughSpaceNotStickyHun_minHeight() {
setGapHeight(0f)
// No divider height since we're testing one element where index = 0
@@ -296,6 +341,26 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+ fun getSpaceNeeded_onLockscreenSavingSpacePromotedOngoing_minHeight() {
+ setGapHeight(0f)
+ // No divider height since we're testing one element where index = 0
+
+ val expandableView = createMockRow(10f, isPromotedOngoing = true)
+ whenever(expandableView.getMinHeight(any())).thenReturn(5)
+
+ val space =
+ sizeCalculator.getSpaceNeeded(
+ expandableView,
+ visibleIndex = 0,
+ previousView = null,
+ stack = stackLayout,
+ onLockscreen = true,
+ )
+ assertThat(space.whenSavingSpace).isEqualTo(5)
+ }
+
+ @Test
fun getSpaceNeeded_onLockscreenSavingSpaceNotStickyHun_minHeight() {
setGapHeight(0f)
// No divider height since we're testing one element where index = 0
@@ -366,6 +431,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
isSticky: Boolean = false,
isRemoved: Boolean = false,
visibility: Int = VISIBLE,
+ isPromotedOngoing: Boolean = false,
): ExpandableNotificationRow {
val row = mock(ExpandableNotificationRow::class.java)
val entry = mock(NotificationEntry::class.java)
@@ -378,6 +444,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
whenever(row.getMinHeight(any())).thenReturn(height.toInt())
whenever(row.intrinsicHeight).thenReturn(height.toInt())
whenever(row.heightWithoutLockscreenConstraints).thenReturn(height.toInt())
+ whenever(row.isPromotedOngoing).thenReturn(isPromotedOngoing)
return row
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
index 43ad042ecf78..57b7df7a8d31 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
@@ -162,7 +162,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
Mockito.`when`(iconManagerFactory.create(ArgumentMatchers.any(), ArgumentMatchers.any()))
.thenReturn(iconManager)
- Mockito.`when`(statusBarContentInsetsProviderStore.defaultDisplay)
+ Mockito.`when`(statusBarContentInsetsProviderStore.forDisplay(context.displayId))
.thenReturn(kosmos.mockStatusBarContentInsetsProvider)
allowTestableLooperAsMainThread()
looper.runWithLooper {
@@ -180,6 +180,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
private fun createController(): KeyguardStatusBarViewController {
return KeyguardStatusBarViewController(
kosmos.testDispatcher,
+ context,
keyguardStatusBarView,
carrierTextController,
configurationController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/SwitchAppsGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/SwitchAppsGestureRecognizerTest.kt
new file mode 100644
index 000000000000..756b79c6a868
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/SwitchAppsGestureRecognizerTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.touchpad.tutorial.ui.gesture
+
+import android.view.MotionEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
+import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SwitchAppsGestureRecognizerTest : SysuiTestCase() {
+
+ private var gestureState: GestureState = NotStarted
+ private val gestureRecognizer =
+ SwitchAppsGestureRecognizer(gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt())
+
+ @Before
+ fun before() {
+ gestureRecognizer.addGestureStateCallback { gestureState = it }
+ }
+
+ @Test
+ fun triggersProgressRelativeToDistanceWhenSwipingRight() {
+ assertProgressWhileMovingFingers(
+ deltaX = SWIPE_DISTANCE / 2,
+ expected = InProgress(progress = 0.5f),
+ )
+ assertProgressWhileMovingFingers(
+ deltaX = SWIPE_DISTANCE,
+ expected = InProgress(progress = 1f),
+ )
+ }
+
+ @Test
+ fun triggersProgressDoesNotGoBelowZero() {
+ // going in the wrong direction
+ assertProgressWhileMovingFingers(
+ deltaX = -SWIPE_DISTANCE,
+ expected = InProgress(progress = 0f),
+ )
+ }
+
+ @Test
+ fun triggersProgressDoesNotExceedOne() {
+ // going further than required distance
+ assertProgressWhileMovingFingers(
+ deltaX = SWIPE_DISTANCE * 2,
+ expected = InProgress(progress = 1f),
+ )
+ }
+
+ private fun assertProgressWhileMovingFingers(deltaX: Float, expected: InProgress) {
+ assertStateAfterEvents(
+ events = FourFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) },
+ expectedState = expected,
+ )
+ }
+
+ private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
+ events.forEach { gestureRecognizer.accept(it) }
+ assertThat(gestureState).isEqualTo(expectedState)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureScreenViewModelTest.kt
new file mode 100644
index 000000000000..ee339b0b7446
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureScreenViewModelTest.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.touchpad.tutorial.ui.viewmodel
+
+import android.content.res.mockResources
+import android.view.MotionEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.android.systemui.touchpad.tutorial.ui.gesture.FourFingerGesture
+import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
+import com.android.systemui.touchpad.ui.gesture.touchpadGestureResources
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SwitchAppsGestureScreenViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val resources = kosmos.mockResources
+ private val fakeConfigRepository = kosmos.fakeConfigurationRepository
+ private val viewModel =
+ SwitchAppsGestureScreenViewModel(
+ GestureRecognizerAdapter(
+ SwitchAppsGestureRecognizerProvider(kosmos.touchpadGestureResources),
+ kosmos.inputDeviceTutorialLogger,
+ )
+ )
+
+ @Before
+ fun before() {
+ setDistanceThreshold(threshold = SWIPE_DISTANCE - 1)
+ kosmos.useUnconfinedTestDispatcher()
+ }
+
+ @Test
+ fun emitsProgressStateWithProgressAnimation() =
+ kosmos.runTest {
+ assertProgressWhileMovingFingers(
+ deltaX = SWIPE_DISTANCE,
+ expected =
+ InProgress(
+ progress = 1f,
+ startMarker = "gesture to R",
+ endMarker = "end of gesture",
+ ),
+ )
+ }
+
+ @Test
+ fun emitsFinishedStateWithSuccessAnimation() =
+ kosmos.runTest {
+ assertStateAfterEvents(
+ events = FourFingerGesture.swipeRight(),
+ expected = Finished(successAnimation = R.raw.trackpad_switch_apps_success),
+ )
+ }
+
+ @Test
+ fun emitErrorStateWhenLatestDistanceThresholdNotReached() =
+ kosmos.runTest {
+ fun performGesture() =
+ FourFingerGesture.swipeRight().forEach { viewModel.handleEvent(it) }
+ val state by collectLastValue(viewModel.tutorialState)
+ performGesture()
+ assertThat(state).isInstanceOf(Finished::class.java)
+
+ setDistanceThreshold(SWIPE_DISTANCE + 1)
+ performGesture() // now swipe distance is not enough to trigger success
+
+ assertThat(state).isInstanceOf(Error::class.java)
+ }
+
+ private fun setDistanceThreshold(threshold: Float) {
+ whenever(
+ resources.getDimensionPixelSize(
+ R.dimen.touchpad_tutorial_gestures_distance_threshold
+ )
+ )
+ .thenReturn(threshold.toInt())
+ fakeConfigRepository.onAnyConfigurationChange()
+ }
+
+ private fun Kosmos.assertProgressWhileMovingFingers(
+ deltaX: Float,
+ expected: TutorialActionState,
+ ) {
+ assertStateAfterEvents(
+ events = FourFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) },
+ expected = expected,
+ )
+ }
+
+ private fun Kosmos.assertStateAfterEvents(
+ events: List<MotionEvent>,
+ expected: TutorialActionState,
+ ) {
+ val state by collectLastValue(viewModel.tutorialState)
+ events.forEach { viewModel.handleEvent(it) }
+ assertThat(state).isEqualTo(expected)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
index ba6ea9f5e8bb..89410593fe62 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
@@ -16,11 +16,14 @@
package com.android.systemui.wallpapers
+import android.app.Flags
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.service.wallpaper.WallpaperService.Engine
import android.testing.TestableLooper.RunWithLooper
import android.view.Surface
@@ -37,6 +40,7 @@ import org.mockito.Mockito.spy
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyZeroInteractions
import org.mockito.kotlin.whenever
@SmallTest
@@ -70,6 +74,18 @@ class GradientColorWallpaperTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+ fun onSurfaceRedrawNeeded_flagDisabled_shouldNotDrawInCanvas() {
+ val engine = createGradientColorWallpaperEngine()
+ engine.onCreate(surfaceHolder)
+
+ engine.onSurfaceRedrawNeeded(surfaceHolder)
+
+ verifyZeroInteractions(canvas)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
fun onSurfaceRedrawNeeded_shouldDrawInCanvas() {
val engine = createGradientColorWallpaperEngine()
engine.onCreate(surfaceHolder)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
index 111c232280c3..2985053f56d5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
@@ -19,10 +19,11 @@ package com.android.systemui.wallpapers.data.repository
import android.app.WallpaperInfo
import android.view.View
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
/** Fake implementation of the wallpaper repository. */
class FakeWallpaperRepository : WallpaperRepository {
override val wallpaperInfo = MutableStateFlow<WallpaperInfo?>(null)
- override val wallpaperSupportsAmbientMode = MutableStateFlow(false)
+ override val wallpaperSupportsAmbientMode = flowOf(false)
override var rootView: View? = null
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
index b8dd334dcad9..03753d9aa884 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
@@ -24,11 +24,13 @@ import android.content.pm.UserInfo
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.R
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardClockRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.shared.Flags as SharedFlags
import com.android.systemui.user.data.model.SelectedUserModel
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.FakeUserRepository
@@ -74,10 +76,6 @@ class WallpaperRepositoryImplTest : SysuiTestCase() {
@Before
fun setUp() {
whenever(wallpaperManager.isWallpaperSupported).thenReturn(true)
- context.orCreateTestableResources.addOverride(
- com.android.internal.R.bool.config_dozeSupportsAodWallpaper,
- true,
- )
}
@Test
@@ -225,214 +223,29 @@ class WallpaperRepositoryImplTest : SysuiTestCase() {
}
@Test
- fun wallpaperInfo_deviceDoesNotSupportAmbientWallpaper_alwaysFalse() =
+ @EnableFlags(SharedFlags.FLAG_AMBIENT_AOD)
+ fun wallpaperSupportsAmbientMode_deviceDoesNotSupport_false() =
testScope.runTest {
context.orCreateTestableResources.addOverride(
- com.android.internal.R.bool.config_dozeSupportsAodWallpaper,
+ R.bool.config_dozeSupportsAodWallpaper,
false,
)
- val latest by collectLastValue(underTest.wallpaperInfo)
- assertThat(latest).isNull()
-
- // Even WHEN there *is* current wallpaper
- val wp1 = mock<WallpaperInfo>()
- whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(wp1)
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
- context,
- Intent(Intent.ACTION_WALLPAPER_CHANGED),
- )
-
- // THEN the value is still null because wallpaper isn't supported
- assertThat(latest).isNull()
- }
-
- @Test
- fun wallpaperSupportsAmbientMode_nullInfo_false() =
- testScope.runTest {
- val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
-
- whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(null)
-
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
- context,
- Intent(Intent.ACTION_WALLPAPER_CHANGED),
- )
-
- assertThat(latest).isFalse()
- }
-
- @Test
- fun wallpaperSupportsAmbientMode_infoDoesNotSupport_false() =
- testScope.runTest {
- val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
-
- whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(UNSUPPORTED_WP)
-
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
- context,
- Intent(Intent.ACTION_WALLPAPER_CHANGED),
- )
-
- assertThat(latest).isFalse()
- }
-
- @Test
- fun wallpaperSupportsAmbientMode_infoSupports_true() =
- testScope.runTest {
- val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
-
- whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP)
-
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
- context,
- Intent(Intent.ACTION_WALLPAPER_CHANGED),
- )
-
- assertThat(latest).isTrue()
- }
-
- @Test
- fun wallpaperSupportsAmbientMode_initialValueIsFetched_true() =
- testScope.runTest {
- whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id))
- .thenReturn(SUPPORTED_WP)
- userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP))
- userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP)
-
- // Start up the repo and let it run the initial fetch
- underTest.wallpaperSupportsAmbientMode
- runCurrent()
-
- // WHEN the repo initially starts up (underTest is lazy), then it fetches the current
- // value for the wallpaper
- assertThat(underTest.wallpaperSupportsAmbientMode.value).isTrue()
- }
-
- @Test
- fun wallpaperSupportsAmbientMode_initialValueIsFetched_false() =
- testScope.runTest {
- whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_UNSUPPORTED_WP.id))
- .thenReturn(UNSUPPORTED_WP)
- userRepository.setUserInfos(listOf(USER_WITH_UNSUPPORTED_WP))
- userRepository.setSelectedUserInfo(USER_WITH_UNSUPPORTED_WP)
-
- // WHEN the repo initially starts up (underTest is lazy), then it fetches the current
- // value for the wallpaper
- assertThat(underTest.wallpaperSupportsAmbientMode.value).isFalse()
- }
-
- @Test
- fun wallpaperSupportsAmbientMode_updatesOnUserChanged() =
- testScope.runTest {
- val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
-
- whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id))
- .thenReturn(SUPPORTED_WP)
- whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_UNSUPPORTED_WP.id))
- .thenReturn(UNSUPPORTED_WP)
- userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP, USER_WITH_UNSUPPORTED_WP))
-
- // WHEN a user with supported wallpaper is selected
- userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP)
-
- // THEN it's true
- assertThat(latest).isTrue()
-
- // WHEN the user is switched to a user with unsupported wallpaper
- userRepository.setSelectedUserInfo(USER_WITH_UNSUPPORTED_WP)
-
- // THEN it's false
- assertThat(latest).isFalse()
- }
-
- @Test
- fun wallpaperSupportsAmbientMode_doesNotUpdateOnUserChanging() =
- testScope.runTest {
- val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
-
- whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id))
- .thenReturn(SUPPORTED_WP)
- whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_UNSUPPORTED_WP.id))
- .thenReturn(UNSUPPORTED_WP)
- userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP, USER_WITH_UNSUPPORTED_WP))
-
- // WHEN a user with supported wallpaper is selected
- userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP)
-
- // THEN it's true
- assertThat(latest).isTrue()
-
- // WHEN the user has started switching to a user with unsupported wallpaper but hasn't
- // finished yet
- userRepository.selectedUser.value =
- SelectedUserModel(USER_WITH_UNSUPPORTED_WP, SelectionStatus.SELECTION_IN_PROGRESS)
-
- // THEN it still matches the old user
- assertThat(latest).isTrue()
- }
-
- @Test
- fun wallpaperSupportsAmbientMode_updatesOnIntent() =
- testScope.runTest {
- val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
-
- whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(UNSUPPORTED_WP)
-
- assertThat(latest).isFalse()
-
- // WHEN the info now supports ambient mode and a broadcast is sent
- whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP)
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
- context,
- Intent(Intent.ACTION_WALLPAPER_CHANGED),
- )
-
- // THEN the flow updates
- assertThat(latest).isTrue()
- }
-
- @Test
- fun wallpaperSupportsAmbientMode_wallpaperNotSupported_alwaysFalse() =
- testScope.runTest {
- whenever(wallpaperManager.isWallpaperSupported).thenReturn(false)
-
val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
assertThat(latest).isFalse()
-
- // Even WHEN the current wallpaper *does* support ambient mode
- whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP)
-
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
- context,
- Intent(Intent.ACTION_WALLPAPER_CHANGED),
- )
-
- // THEN the value is still false because wallpaper isn't supported
- assertThat(latest).isFalse()
}
@Test
- fun wallpaperSupportsAmbientMode_deviceDoesNotSupportAmbientWallpaper_alwaysFalse() =
+ @EnableFlags(SharedFlags.FLAG_AMBIENT_AOD)
+ fun wallpaperSupportsAmbientMode_deviceDoesSupport_true() =
testScope.runTest {
context.orCreateTestableResources.addOverride(
- com.android.internal.R.bool.config_dozeSupportsAodWallpaper,
- false,
+ R.bool.config_dozeSupportsAodWallpaper,
+ true,
)
val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
- assertThat(latest).isFalse()
-
- // Even WHEN the current wallpaper *does* support ambient mode
- whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP)
-
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
- context,
- Intent(Intent.ACTION_WALLPAPER_CHANGED),
- )
-
- // THEN the value is still false because the device doesn't support it
- assertThat(latest).isFalse()
+ assertThat(latest).isTrue()
}
@Test
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 433c0a71008d..e7d6b2fe08f4 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -159,6 +159,17 @@
<item name="android:shadowRadius">?attr/shadowRadius</item>
</style>
+ <style name="TextAppearance.Keyguard.BottomArea.DoubleShadow">
+ <item name="keyShadowBlur">0.5dp</item>
+ <item name="keyShadowOffsetX">0.5dp</item>
+ <item name="keyShadowOffsetY">0.5dp</item>
+ <item name="keyShadowAlpha">0.8</item>
+ <item name="ambientShadowBlur">0.5dp</item>
+ <item name="ambientShadowOffsetX">0.5dp</item>
+ <item name="ambientShadowOffsetY">0.5dp</item>
+ <item name="ambientShadowAlpha">0.6</item>
+ </style>
+
<style name="TextAppearance.Keyguard.BottomArea.Button">
<item name="android:shadowRadius">0</item>
</style>
diff --git a/packages/SystemUI/res/drawable-nodpi/hub_onboarding_bg.png b/packages/SystemUI/res/drawable-nodpi/hub_onboarding_bg.png
new file mode 100644
index 000000000000..bd55e83b377b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/hub_onboarding_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_widgets.xml b/packages/SystemUI/res/drawable/ic_widgets.xml
deleted file mode 100644
index 9e05809bfb33..000000000000
--- a/packages/SystemUI/res/drawable/ic_widgets.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT 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:tint="?attr/colorControlNormal"
- android:viewportHeight="960"
- android:viewportWidth="960">
- <path
- android:fillColor="@android:color/black"
- android:pathData="M666,520L440,294L666,68L892,294L666,520ZM120,440L120,120L440,120L440,440L120,440ZM520,840L520,520L840,520L840,840L520,840ZM120,840L120,520L440,520L440,840L120,840ZM200,360L360,360L360,200L200,200L200,360ZM667,408L780,295L667,182L554,295L667,408ZM600,760L760,760L760,600L600,600L600,760ZM200,760L360,760L360,600L200,600L200,760ZM360,360L360,360L360,360L360,360L360,360ZM554,295L554,295L554,295L554,295L554,295ZM360,600L360,600L360,600L360,600L360,600ZM600,600L600,600L600,600L600,600L600,600Z" />
-</vector>
diff --git a/packages/SystemUI/res/layout/media_projection_recent_tasks.xml b/packages/SystemUI/res/layout/media_projection_recent_tasks.xml
index 31baf26e4a1b..3c810a43a3a7 100644
--- a/packages/SystemUI/res/layout/media_projection_recent_tasks.xml
+++ b/packages/SystemUI/res/layout/media_projection_recent_tasks.xml
@@ -51,5 +51,5 @@
android:layout_marginTop="24dp"
android:importantForAccessibility="no"
android:src="@*android:drawable/ic_drag_handle"
- android:tint="?android:attr/textColorSecondary" />
+ android:tint="@*android:color/materialColorSecondary" />
</LinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 1c2226925191..91d92a21ddf1 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Dateer tans op"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Jy sal nie jou volgende wekker <xliff:g id="WHEN">%1$s</xliff:g> hoor nie"</string>
<string name="alarm_template" msgid="2234991538018805736">"om <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"op <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Stelselapps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Verrigting van veelvuldige take"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Verdeelde skerm"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Invoer"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Appkortpaaie"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Huidige app"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Pasmaak kortpaaie"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Verwyder kortpad?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Stel terug na verstek?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Druk die handelingsleutel en een of meer ander sleutels saam om hierdie kortpad te skep"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Dit sal jou gepasmaakte kortpad permanent uitvee."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Dit sal al jou gepasmaakte kortpaaie permanent uitvee."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Soekkortpaaie"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, stel terug"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Kanselleer"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Druk sleutel"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Sleutelkombinasie is reeds in gebruik. Probeer ’n ander kombinasie."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Kortpad kan nie gestel word nie."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Voeg kortpad by"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Gaan terug"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Gaan na tuisskerm"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Bekyk onlangse apps"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Wissel apps"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klaar"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Probeer weer!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gaan terug"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Knap gedaan!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Jy het die Bekyk Onlangse Apps-gebaar voltooi."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Swiep op en hou met drie vingers op jou raakpaneel om onlangse apps te bekyk"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Wissel apps"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Uitstekende werk!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Jy het die “wissel tussen apps”-gebaar voltooi."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Bekyk alle apps"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Druk die handelingsleutel op jou sleutelbord"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 991f60a3a445..6e079a2a647f 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"በማዘመን ላይ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"የስራ መገለጫ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"የአውሮፕላን ሁነታ"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"የእርስዎን ቀጣይ ማንቂያ <xliff:g id="WHEN">%1$s</xliff:g> አይሰሙም"</string>
<string name="alarm_template" msgid="2234991538018805736">"በ<xliff:g id="WHEN">%1$s</xliff:g> ላይ"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"በ<xliff:g id="WHEN">%1$s</xliff:g> ላይ"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"የሥርዓት መተግበሪያዎች"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ብዙ ተግባራትን በተመሳሳይ ጊዜ ማከናወን"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"የተከፈለ ማያ ገፅ"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ግብዓት"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"የመተግበሪያ አቋራጮች"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"የአሁን መተግበሪያ"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"አቋራጮችን ያብጁ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"አቋራጭ ይወገድ?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ወደ ነባሪ ዳግም ይጀመር?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"ይህን አቋራጭ ለመፍጠር የእርምጃ ቁልፉን እና ላይ አንድ ወይም ከዚያ በላይ ሌሎቹ ቁልፎችን አንድ ላይ ይጫኑ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ይህ ብጁ አቋራጭዎን በቋሚነት ይሰርዛል።"</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ይህ ሁሉንም ብጁ አቋራጮችዎን በቋሚነት ይሰርዛል።"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"የፍለጋ አቋራጮች"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"አዎ፣ ዳግም አስጀምር"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ይቅር"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ቁልፍ ይጫኑ"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"የቁልፍ ጥምረት ቀድሞውኑ ጥቅም ላይ ውሏል። ሌላ ጥምረት ይሞክሩ።"</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"አቋራጩ ሊቀናበር አይችልም።"</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"አቋራጭ አክል"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ወደኋላ ተመለስ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ወደ መነሻ ሂድ"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"የቅርብ ጊዜ መተግበሪያዎችን አሳይ"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"መተግበሪያዎችን ይቀያይሩ"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ተከናውኗል"</string>
<string name="gesture_error_title" msgid="469064941635578511">"እንደገና ይሞክሩ!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ወደኋላ ተመለስ"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ጥሩ ሠርተዋል!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"የቅርብ ጊዜ መተግበሪያዎች አሳይ ምልክትን አጠናቅቀዋል።"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"የቅርብ ጊዜ መተግበሪያዎችን ለማየት የመዳሰሻ ሰሌዳዎ ላይ ሦስት ጣቶችን በመጠቀም ወደላይ ያንሸራትቱ እና ይያዙ"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"መተግበሪያዎችን ይቀያይሩ"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"ጥሩ ሠርተዋል!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"የመተግበሪያ ምልክቶችን ይቀይሩ የሚለወን አጠናቅቀዋል።"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"ሁሉንም መተግበሪያዎች ይመልከቱ"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"በቁልፍ ሰሌዳዎ ላይ ያለውን የተግባር ቁልፍ ይጫኑ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 2917cfcee872..7896cdbd68f8 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"جارٍ تعديل الحالة"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ملف العمل"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطيران"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"لن تسمع المنبّه القادم في <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"في <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"يوم <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"تطبيقات النظام"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"تعدُّد المهام"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"تقسيم الشاشة"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"الإدخال"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"اختصارات التطبيقات"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"التطبيق الحالي"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"تخصيص الاختصارات"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"هل تريد إزالة هذا الاختصار؟"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"يُرجى تأكيد إعادة الضبط على الإعدادات التلقائية"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"لإنشاء هذا الاختصار، يُرجى الضغط على مفتاح الإجراء ومفتاح آخر أو أكثر معًا"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"سيؤدي هذا الإجراء إلى حذف الاختصار المخصّص نهائيًا."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"سيؤدي هذا الإجراء إلى حذف جميع الاختصارات المخصّصة نهائيًا."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"البحث في الاختصارات"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"نعم، أريد إعادة الضبط"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"إلغاء"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"اضغط على مفتاح"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"يتم حاليًا استخدام مجموعة المفاتيح هذه. يُرجى تجربة مجموعة أخرى من المفاتيح."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"تعذَّر ضبط الاختصار."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"إضافة اختصار"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"رجوع"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"الانتقال إلى الصفحة الرئيسية"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"عرض التطبيقات المستخدَمة مؤخرًا"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"التبديل بين التطبيقات"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"تم"</string>
<string name="gesture_error_title" msgid="469064941635578511">"يُرجى إعادة المحاولة"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"رجوع"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"أحسنت."</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"لقد أكملْت التدريب على إيماءة عرض التطبيقات المستخدَمة مؤخرًا."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"لعرض التطبيقات المستخدَمة مؤخرًا، يُرجى التمرير سريعًا للأعلى مع الاستمرار باستخدام ثلاثة أصابع على لوحة اللمس"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"التبديل بين التطبيقات"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"أحسنت صنعًا."</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"أكملت التدريب على إيماءة التبديل بين التطبيقات."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"عرض جميع التطبيقات"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"اضغط على مفتاح الإجراء في لوحة المفاتيح"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index ec05cb4f37eb..7c99df9a8c78 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"আপডে’ট কৰি থকা হৈছে"</string>
<string name="status_bar_work" msgid="5238641949837091056">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"এয়াৰপ্লে’ন ম’ড"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"আপুনি আপোনাৰ পিছৰটো এলাৰ্ম <xliff:g id="WHEN">%1$s</xliff:g> বজাত শুনা নাপাব"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> বজাত"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> বজাত"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ছিষ্টেম এপ্‌"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"মাল্টিটাস্কিং"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"বিভাজিত স্ক্ৰীন"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ইনপুট"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"এপ্ শ্বৰ্টকাটসমূহ"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"বৰ্তমানৰ এপ্‌"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"উভতি যাওক"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"গৃহ পৃষ্ঠালৈ যাওক"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"শেহতীয়া এপ্‌সমূহ চাওক"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"এপ্‌সমূহ সলনি কৰক"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"হ’ল"</string>
<string name="gesture_error_title" msgid="469064941635578511">"পুনৰ চেষ্টা কৰক!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"উভতি যাওক"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"বঢ়িয়া!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"আপুনি শেহতীয়া এপ্ চোৱাৰ নিৰ্দেশনাটো সম্পূৰ্ণ কৰিছে।"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"শেহতীয়া এপ্‌সমূহ চাবলৈ, আপোনাৰ টাচ্চপেডত তিনিটা আঙুলি ব্যৱহাৰ কৰি ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"এপ্‌সমূহ সলনি কৰক"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"বঢ়িয়া!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"আপুনি এপ্‌ সলনি কৰাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে।"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"আটাইবোৰ এপ্ চাওক"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"আপোনাৰ কীব’ৰ্ডৰ কাৰ্য কীটোত টিপক"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index a76a9cc0e281..64b2726f26d8 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Güncəllənir"</string>
<string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Təyyarə rejimi"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> zaman növbəti xəbərdarlığınızı eşitməyəcəksiniz"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistem tətbiqləri"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Çoxsaylı tapşırıq icrası"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Bölünmüş ekran"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Daxiletmə"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Tətbiq qısayolları"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Cari tətbiq"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Qısayolları fərdiləşdirin"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Qısayol silinsin?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Defolt vəziyyətə qaytarılsın?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Bu qısayolu yaratmaq üçün Fəaliyyət düyməsini, habelə bir və ya bir neçə digər düyməni birlikdə basın"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Bu, fərdi qısayolunuzu həmişəlik siləcək."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Bu, bütün fərdi qısayollarınızı həmişəlik siləcək."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Axtarış qısayolları"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Bəli, sıfırlayın"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Ləğv edin"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Düyməni basın"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Düymə kombinasiyası artıq istifadə olunur. Başqa kombinasiyanı sınayın."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Qısayol ayarlana bilməz."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Qısayol əlavə edin"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Geri qayıdın"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Əsas səhifəyə keçin"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Son tətbiqlərə baxın"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Başqa tətbiqə keçin"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hazırdır"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Yenidən cəhd edin!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri qayıdın"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Əla!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Son tətbiqlərə baxmaq jestini tamamladınız."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Ən son tətbiqlərə baxmaq üçün taçpeddə üç barmağınızla yuxarı çəkin və saxlayın"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Başqa tətbiqə keçin"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Əla!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Tətbiqlərarası keçid jestini tamamladınız."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Bütün tətbiqlərə baxın"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klaviaturada fəaliyyət açarına basın"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 2dfb37bdf79f..31566929dae0 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ažurira se"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Poslovni profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim rada u avionu"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sledeći alarm u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemske aplikacije"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Obavljanje više zadataka istovremeno"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podeljeni ekran"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečice za aplikacije"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuelna aplikacija"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Nazad"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Idi na početni ekran"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Prikaži nedavno korišćene aplikacije"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Pređi na drugu aplikaciju"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Probajte ponovo."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazad"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Odlično!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Dovršili ste pokret za prikazivanje nedavno korišćenih aplikacija."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Da biste pregledali nedavne aplikacije, prevucite nagore i zadržite sa tri prsta na tačpedu"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Pređi na drugu aplikaciju"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Odlično!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Dovršili ste pokret za promenu aplikacija."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Prikaži sve aplikacije"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite taster radnji na tastaturi"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 33b22c397d8d..5e339a4ea07e 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Абнаўленне…"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Працоўны профіль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Рэжым палёту"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Вы не пачуеце наступны будзільнік <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Сістэмныя праграмы"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Шматзадачнасць"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Падзелены экран"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Увод"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Ярлыкі праграм"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Бягучая праграма"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Наладзіць спалучэнні клавіш"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Выдаліць спалучэнне клавіш?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Скінуць налады да стандартных?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Каб стварыць гэта спалучэнне клавіш, націсніце клавішу дзеяння разам з яшчэ адной ці некалькімі клавішамі"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Гэта дзеянне назаўсёды выдаліць прызначанае вамі спалучэнне клавіш."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Усе карыстальніцкія спалучэнні клавіш будуць назаўсёды выдалены."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пошук спалучэнняў клавіш"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Так, скінуць"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Скасаваць"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Націсніце клавішу"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Гэта спалучэнне клавіш ужо выкарыстоўваецца. Паспрабуйце іншае спалучэнне."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Не ўдаецца наладзіць спалучэнне клавіш."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Дадаць ярлык"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"На галоўную старонку"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прагляд нядаўніх праграм"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Пераключэнне праграм"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Гатова"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Паспрабуйце яшчэ раз!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Выдатная праца!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Вы скончылі вывучэнне жэсту для прагляду нядаўніх праграм."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Каб праглядзець нядаўнія праграмы, правядзіце трыма пальцамі ўверх па сэнсарным экране і затрымайце пальцы"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Пераключэнне праграм"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Выдатная праца!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Вы навучыліся рабіць жэст пераключэння праграм."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Глядзець усе праграмы"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Націсніце клавішу дзеяння на клавіятуры"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 7733002996b1..245711f831b5 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Актуализира се"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Потребителски профил в Work"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Самолетен режим"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Няма да чуете следващия си будилник в <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"в <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"в/ъв <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системни приложения"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Няколко задачи едновременно"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Разделен екран"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Въвеждане"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Преки пътища към приложения"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Текущо приложение"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Персонализиране на преките пътища"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Да се премахне ли клавишната комбинация?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Да се възстановят ли стандартните настройки?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"За да създадете тази клавишна комбинация, натиснете клавиша за действия заедно с един или повече други клавиши"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Това ще изтрие персонализираната клавишна комбинация за постоянно."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Това ще изтрие всичките ви персонализирани преки пътища за постоянно."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Търсете клавишни комбинации"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Да, нулиране"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Отказ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Натиснете клавиш"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Клавишната комбинация вече се използва. Опитайте с друга."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Прекият път не може да се зададе."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Добавяне на пряк път"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Към началния екран"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Преглед на скорошните приложения"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Превключване на приложенията"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Опитайте отново!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Отлично!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Изпълнихте жеста за преглед на скорошните приложения."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"За да прегледате скорошните приложения, плъзнете три пръста нагоре по сензорния панел и задръжте"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Превключване на приложенията"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Отлично!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Изпълнихте жеста за превключване между приложения."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Преглед на всички приложения"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Натиснете клавиша за действия на клавиатурата си"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index d1f81e2df0cf..42a4d540c0c8 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"আপডেট করা হচ্ছে"</string>
<string name="status_bar_work" msgid="5238641949837091056">"কাজের প্রোফাইল"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"বিমান মোড"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"আপনি আপনার পরবর্তী <xliff:g id="WHEN">%1$s</xliff:g> অ্যালার্ম শুনতে পাবেন না"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> -তে"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"সিস্টেম অ্যাপ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"মাল্টিটাস্কিং"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"স্প্লিট স্ক্রিন"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ইনপুট"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"অ্যাপ শর্টকাট"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"বর্তমান অ্যাপ"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"শর্টকাট কাস্টমাইজ করুন"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"শর্টকাট সরাবেন?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ডিফল্ট শর্টকার্ট আবার রিসেট করতে চান?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"এই শর্টকার্ট তৈরি করতে, অ্যাকশন \'কী\' এবং এক বা আরও বেশি \'কী\' একসাথে প্রেস করুন"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"এটি আপনার কাস্টম শর্টকাট স্থায়ীভাবে মুছে ফেলবে।"</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"এটি আপনার সব কাস্টম শর্টকার্ট স্থায়ীভাবে মুছে দেবে।"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"শর্টকাট সার্চ করুন"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"হ্যাঁ, রিসেট করতে চাই"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"বাতিল করুন"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"কী প্রেস করুন"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"\'কী\' শর্টকার্ট আগে থেকে ব্যবহার হচ্ছে। অন্য শর্টকার্ট ব্যবহার করে দেখুন।"</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"শর্টকাট সেট করা যায়নি।"</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"শর্টকাট যোগ করুন"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ফিরে যান"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"হোমে যান"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"সম্প্রতি ব্যবহার করা হয়েছে এমন অ্যাপ দেখুন"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"অ্যাপ পরিবর্তন করুন"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"হয়ে গেছে"</string>
<string name="gesture_error_title" msgid="469064941635578511">"আবার চেষ্টা করুন!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ফিরে যান"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"অসাধারণ!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"সম্প্রতি ব্যবহার করা হয়েছে এমন অ্যাপের জেসচার দেখা সম্পূর্ণ করেছেন।"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"সাম্প্রতিক অ্যাপ দেখতে, নিজের টাচপ্যাডে তিনটি আঙুল ব্যবহার করে উপরের দিকে সোয়াইপ করে হোল্ড করুন"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"অ্যাপ পরিবর্তন করুন"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"অসাধারণ!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"আপনি অ্যাপ পরিবর্তন করার জেসচার সম্পূর্ণ করেছেন।"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"সব অ্যাপ দেখুন"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"আপনার কীবোর্ডে অ্যাকশন কী প্রেস করুন"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 9d128b4b1562..69f163512987 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ažuriranje"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Radni profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način rada u avionu"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sljedeći alarm u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemske aplikacije"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podijeljeni ekran"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečice aplikacije"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutna aplikacija"</string>
@@ -1438,7 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Prilagodite prečice"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Ukloniti prečicu?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vratiti na zadano?"</string>
- <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Da biste izradili taj prečac, pritisnite tipku za radnju i jednu ili više drugih tipki zajedno"</string>
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Da kreirate ovu prečicu, istovremeno pritisnite tipku radnji i jednu ili više tipki"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ovo će trajno izbrisati prilagođenu prečicu."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Ovo će trajno izbrisati sve vaše prilagođene prečice."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pretražite prečice"</string>
@@ -1460,7 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, vrati na zadano"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Otkaži"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite tipku"</string>
- <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Ta se kombinacija već koristi. Pokušajte s nekom drugom kombinacijom."</string>
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Ta se kombinacija tipki već koristi. Pokušajte s drugom kombinacijom."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Prečica se ne može postaviti."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Dodavanje prečice"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Nazad"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Odlazak na početni ekran"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Prikaži nedavne aplikacije"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Promijenite aplikaciju"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Pokušajte ponovo!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazad"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Sjajno!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Izvršili ste pokret za prikaz nedavnih aplikacija."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Da pogledate nedavne aplikacije, prevucite nagore i zadržite s tri prsta na dodirnoj podlozi"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Promijenite aplikaciju"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Sjajno!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Izvršili ste pokret za promjenu aplikacije."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Pogledajte sve aplikacije"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipku radnji na tastaturi"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index b16df77784e6..d90c4fffd5aa 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"S\'està actualitzant"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de treball"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode d\'avió"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> no sentiràs la pròxima alarma"</string>
<string name="alarm_template" msgid="2234991538018805736">"Hora: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"Dia: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicacions del sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasca"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Dreceres d\'aplicacions"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicació actual"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Personalitza les dreceres"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vols suprimir la drecera?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vols restablir els valors predeterminats?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Per crear aquesta drecera, prem la tecla d\'acció i una o més tecles alhora"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Aquesta acció suprimirà la drecera personalitzada permanentment."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Aquesta acció suprimirà totes les dreceres personalitzades permanentment."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Dreceres de cerca"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sí, restableix"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel·la"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Prem una tecla"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"La combinació de tecles ja s\'està utilitzant. Prova-ho amb una altra combinació."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"No es pot configurar la drecera."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Afegeix una drecera"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Torna"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ves a la pantalla d\'inici"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Mostra les aplicacions recents"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Canviar d\'aplicació"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fet"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Torna-ho a provar"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Torna"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Ben fet!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Has completat el gest per veure les aplicacions recents."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Per veure les aplicacions recents, llisca cap amunt amb tres dits i mantén-los premuts al ratolí tàctil"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Canviar d\'aplicació"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Ben fet!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Has completat el gest per canviar d\'aplicació."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Mostra totes les aplicacions"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Prem la tecla d\'acció al teclat"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 7e1ccdb816c7..2cfc4dab8e48 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Probíhá aktualizace"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Pracovní profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim Letadlo"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Svůj další budík <xliff:g id="WHEN">%1$s</xliff:g> neuslyšíte"</string>
<string name="alarm_template" msgid="2234991538018805736">"v <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systémové aplikace"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Rozdělená obrazovka"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vstup"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Zkratky aplikací"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuální aplikace"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Přizpůsobení zkratek"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Odstranit zkratku?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Resetovat do výchozího nastavení?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"K vytvoření této zkratky stiskněte současně akční klávesu a jednu nebo více dalších kláves"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Vlastní zkratka se trvale smaže."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tím trvale odstraníte všechny své vlastní zkratky."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhledat zkratky"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ano, resetovat"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Zrušit"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Stiskněte klávesu"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Kombinace kláves se už používá. Zkuste jinou kombinaci."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Zkratku není možné nastavit."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Přidat zkratku"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Zpět"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Přejít na domovskou stránku"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Zobrazit nedávné aplikace"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Přepnout aplikace"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hotovo"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Zkuste to znovu."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zpět"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Výborně!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Provedli jste gesto pro zobrazení nedávných aplikací."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Pokud chcete zobrazit poslední aplikace, přejeďte na touchpadu třemi prsty nahoru a podržte je"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Přepnout aplikace"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Výborně!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Dokončili jste gesto přepínání aplikací."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Zobrazit všechny aplikace"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Stiskněte akční klávesu na klávesnici"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 4a3ba0e9342a..29961e340408 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Opdaterer"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Arbejdsprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flytilstand"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Du vil ikke kunne høre din næste alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"kl. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"på <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systemapps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Opdelt skærm"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Appgenveje"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuel app"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Tilpas genveje"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Skal genvejen fjernes?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vil du nulstille til standard?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Opret denne genvej ved at trykke på handlingstasten og én eller flere andre taster samtidigt"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Denne handling sletter din tilpassede genvej permanent."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Denne handling sletter alle dine tilpassede genveje permanent."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Søg efter genveje"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, nulstil"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuller"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tryk på en tast"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Tastekombinationen er allerede i brug. Prøv en anden kombination."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Genvejen kan ikke konfigureres."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Tilføj genvej"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Gå tilbage"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Gå til startsiden"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Se seneste apps"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Skift mellem apps"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Udfør"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Prøv igen!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gå tilbage"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Godt klaret!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du har udført bevægelsen for at se de seneste apps."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Du kan se nyligt brugte apps ved at stryge opad og holde tre fingre nede på touchpladen"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Skift mellem apps"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Godt klaret!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Du har fuldført bevægelsen for at skifte mellem apps."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Se alle apps"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tryk på handlingstasten på dit tastatur"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 32bb148073fa..2e452dda0f34 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Wird aktualisiert…"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Arbeitsprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flugmodus"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Lautloser Weckruf <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"um <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"am <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System-Apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Splitscreen"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Eingabe"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Tastaturkürzel für Apps"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuelle App"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Tastenkombinationen anpassen"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Tastaturkürzel entfernen?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Auf Standardeinstellung zurücksetzen?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Drücke zum Erstellen der Tastenkombination gleichzeitig die Aktionstaste und eine oder mehrere andere Tasten"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Das benutzerdefinierte Tastenkürzel wird endgültig gelöscht."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Alle deine benutzerdefinierten Tastenkürzel werden endgültig gelöscht."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tastenkürzel suchen"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, zurücksetzen"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Abbrechen"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Taste drücken"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Diese Tastenkombination wird bereits verwendet. Versuche es mit einer anderen Kombination."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Die Tastenkombination kann nicht festgelegt werden."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Verknüpfung hinzufügen"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Zurück"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Zum Startbildschirm"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Letzte Apps aufrufen"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Zwischen Apps wechseln"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fertig"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Noch einmal versuchen"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zurück"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Gut gemacht!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du hast das Tutorial für die Touch-Geste zum Aufrufen der zuletzt verwendeten Apps abgeschlossen."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Wenn du zuletzt verwendete Apps aufrufen möchtest, wische mit drei Fingern nach oben und halte das Touchpad gedrückt"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Zwischen Apps wechseln"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Gut gemacht!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Du hast die Touch-Geste „Zwischen Apps wechseln\" abgeschlossen."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Alle Apps anzeigen"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Drücke die Aktionstaste auf deiner Tastatur"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index b26d8623ad2b..c5aa667e9c74 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ενημέρωση"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Προφίλ εργασίας"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Λειτουργία πτήσης"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Δεν θα ακούσετε το επόμενο ξυπνητήρι σας <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"στις <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"στις <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Εφαρμογές συστήματος"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Πολυδιεργασία"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Διαχωρισμός οθόνης"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Εισαγωγή"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Συντομεύσεις εφαρμογών"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Τρέχουσα εφαρμογή"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Προσαρμογή συντομεύσεων"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Κατάργηση συντόμευσης;"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Επαναφορά στις προεπιλογές;"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Για να δημιουργήσετε αυτή τη συντόμευση, πατήστε ταυτόχρονα το πλήκτρο ενέργειας και ένα ή περισσότερα άλλα πλήκτρα"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Με αυτή την ενέργεια, η προσαρμοσμένη συντόμευση θα διαγραφεί οριστικά."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Με αυτή την ενέργεια θα διαγραφούν οριστικά όλες οι προσαρμοσμένες συντομεύσεις."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Συντομεύσεις αναζήτησης"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ναι, επαναφορά"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Ακύρωση"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Πατήστε ένα πλήκτρο"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Ο συνδυασμός πλήκτρων χρησιμοποιείται ήδη. Δοκιμάστε έναν άλλο συνδυασμό."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Δεν είναι δυνατή η ρύθμιση της συντόμευσης."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Προσθήκη συντόμευσης"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Επιστροφή"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Αρχική"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Προβολή πρόσφατων εφαρμογών"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Εναλλαγή μεταξύ εφαρμογών"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Τέλος"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Δοκιμάστε ξανά!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Επιστροφή"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Μπράβο!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ολοκληρώσατε την κίνηση για την προβολή πρόσφατων εφαρμογών."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Για προβολή πρόσφατων εφαρμογών, σύρετε προς τα επάνω με τρία δάχτυλα και κρατήστε τα δάχτυλά σας στην επιφάνεια αφής"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Εναλλαγή μεταξύ εφαρμογών"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Μπράβο!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Ολοκληρώσατε την κίνηση εναλλαγής εφαρμογών."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Προβολή όλων των εφαρμογών"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Πατήστε το πλήκτρο ενέργειας στο πληκτρολόγιό σας"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index ad4e510d20d4..4144dc300a58 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updating"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"at <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"on <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Customise shortcuts"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remove shortcut?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset back to default?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"To create this shortcut, press the action key and one or more other keys together"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"This will delete your custom shortcut permanently."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"This will delete all your custom shortcuts permanently."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Yes, reset"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Key combination already in use. Try another combination."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Shortcut cannot be set."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Add shortcut"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Go back"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Go home"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Switch apps"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Try again."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Well done!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"To view recent apps, swipe up and hold using three fingers on your touchpad"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Switch apps"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Well done!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"You completed the switch apps gesture."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"View all apps"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 7c5328cd5951..b00b01d373f3 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -750,6 +750,7 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updating"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Airplane mode"</string>
+ <string name="status_bar_supervision" msgid="6735015942701134125">"Parental controls"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"at <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"on <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1431,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current App"</string>
@@ -1493,10 +1496,10 @@
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"To view recent apps, swipe up and hold using three fingers on your touchpad"</string>
<string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Switch apps"</string>
- <string name="touchpad_switch_apps_gesture_guidance" msgid="6859830005390732562">"Swipe left or right using four fingers on your touchpad"</string>
+ <string name="touchpad_switch_apps_gesture_guidance" msgid="2751565200937541667">"Swipe left using four fingers on your touchpad"</string>
<string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Great job!"</string>
<string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"You completed the switch apps gesture."</string>
- <string name="touchpad_switch_gesture_error_body" msgid="2211950382592343759">"Swipe left or right using four fingers on your touchpad to switch apps"</string>
+ <string name="touchpad_switch_gesture_error_body" msgid="5508381152326379652">"Swipe left using four fingers on your touchpad to switch apps"</string>
<string name="tutorial_action_key_title" msgid="8172535792469008169">"View all apps"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index ad4e510d20d4..4144dc300a58 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updating"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"at <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"on <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Customise shortcuts"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remove shortcut?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset back to default?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"To create this shortcut, press the action key and one or more other keys together"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"This will delete your custom shortcut permanently."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"This will delete all your custom shortcuts permanently."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Yes, reset"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Key combination already in use. Try another combination."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Shortcut cannot be set."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Add shortcut"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Go back"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Go home"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Switch apps"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Try again."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Well done!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"To view recent apps, swipe up and hold using three fingers on your touchpad"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Switch apps"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Well done!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"You completed the switch apps gesture."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"View all apps"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index ad4e510d20d4..4144dc300a58 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updating"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"at <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"on <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Customise shortcuts"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Remove shortcut?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset back to default?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"To create this shortcut, press the action key and one or more other keys together"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"This will delete your custom shortcut permanently."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"This will delete all your custom shortcuts permanently."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Yes, reset"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Key combination already in use. Try another combination."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Shortcut cannot be set."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Add shortcut"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Go back"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Go home"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Switch apps"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Try again."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Well done!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"To view recent apps, swipe up and hold using three fingers on your touchpad"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Switch apps"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Well done!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"You completed the switch apps gesture."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"View all apps"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 7188fa3701ce..85ff751d28ec 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Actualizando"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avión"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma a la(s) <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"a la(s) <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"el <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps del sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Tareas múltiples"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Accesos directos a aplicaciones"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App actual"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Personalizar combinaciones de teclas"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"¿Quieres quitar la combinación?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"¿Quieres restablecer la configuración predeterminada?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Para crear esta combinación de teclas, presiona la tecla de acción y una o más teclas"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Esta acción borrará tu combinación personalizada de forma permanente."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Esta acción borrará todas tus combinaciones personalizadas de forma permanente."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar combinaciones de teclas"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sí, restablecer"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Presiona una tecla"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"La combinación de teclas ya está en uso. Prueba con otra combinación."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"No se puede establecer la combinación de teclas."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Agregar combinación de teclas"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Atrás"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir a la página principal"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver apps recientes"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Cambiar de app"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Listo"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Vuelve a intentarlo"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atrás"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"¡Bien hecho!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Completaste el gesto para ver las apps recientes."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Para ver las apps recientes, desliza tres dedos hacia arriba y mantenlos presionados en el panel táctil"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Cambia de app"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"¡Bien hecho!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Completaste el gesto para cambiar de app."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas las apps"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Presiona la tecla de acción en el teclado"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 88a6aa461e2f..62c629074ce1 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Actualizando"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo Avión"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string>
<string name="alarm_template" msgid="2234991538018805736">"a las <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicaciones del sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarea"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Accesos directos a aplicaciones"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicación en uso"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Personalizar combinaciones de teclas"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"¿Eliminar combinación de teclas?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"¿Restablecer valores predeterminados?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Para crear esta combinación de teclas, pulsa la tecla de acción y una o varias teclas a la vez"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Se eliminará tu combinación de teclas personalizada de forma permanente."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Se eliminarán todos tus accesos directos personalizados de forma permanente."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar accesos directos"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sí, restablecer"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pulsa una tecla"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"La combinación de teclas ya se está usando. Prueba con otra combinación."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"No se puede configurar la combinación de teclas."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Añadir combinación de teclas"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Volver"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir a Inicio"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver aplicaciones recientes"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Cambiar de aplicación"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hecho"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Vuelve a intentarlo."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atrás"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"¡Bien hecho!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Has completado el gesto para ver las aplicaciones recientes."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Para ver las aplicaciones recientes, desliza tres dedos hacia arriba y mantén pulsado en el panel táctil"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Cambiar de aplicación"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"¡Bien hecho!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Has completado el gesto para cambiar de aplicación."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas las aplicaciones"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pulsa la tecla de acción de tu teclado"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 173a6ab4fa15..920af5c4d353 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Värskendamine"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Tööprofiil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lennukirežiim"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Te ei kuule järgmist äratust kell <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"kell <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"kell <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Süsteemirakendused"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitegumtöö"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Jagatud ekraanikuva"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Sisend"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Rakenduse otseteed"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Praegune rakendus"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Otseteede kohandamine"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Kas soovite otsetee eemaldada?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Kas lähtestada vaikeseadele?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Selle otsetee loomiseks vajutage toiminguklahvi ja ühte või mitut muud klahvi korraga."</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"See kustutab teie kohandatud otsetee jäädavalt."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"See kustutab kõik teie kohandatud otseteed jäädavalt."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsige otseteid"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Lähtesta"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Tühista"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Vajutage klahvi"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Kombinatsioon on juba kasutusel. Proovige mõnda muud kombinatsiooni."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Otseteed ei saa seadistada."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Otsetee lisamine"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Mine tagasi"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Avakuvale"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Hiljutiste rakenduste vaatamine"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Rakenduste vahetamine"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Valmis"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Proovige uuesti!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Tagasi"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Väga hea!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Tegite hiljutiste rakenduste vaatamise liigutuse."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Hiljutiste rakenduste kuvamiseks pühkige puuteplaadil kolme sõrmega üles ja hoidke sõrmi puuteplaadil"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Rakenduste vahetamine"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Väga hea!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Tegite rakenduste vahetamise liigutuse."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Kõigi rakenduste kuvamine"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Vajutage klaviatuuril toiminguklahvi"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 4bf602b8ae4b..912da58efc44 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Eguneratzen"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Laneko profila"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Hegaldi modua"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ez duzu entzungo hurrengo alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string>
<string name="alarm_template" msgid="2234991538018805736">"ordua: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"data: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemaren aplikazioak"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Zeregin bat baino gehiago aldi berean exekutatzea"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantaila zatitzea"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Sarrera"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Aplikazioetarako lasterbideak"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Oraingo aplikazioa"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Pertsonalizatu lasterbideak"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Lasterbidea kendu nahi duzu?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Balio lehenetsia berrezarri nahi duzu?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Lasterbide hau sortzeko, sakatu ekintza-tekla eta beste tekla bat edo gehiago batera"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Betiko ezabatuko da lasterbide pertsonalizatua."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Lasterbide pertsonalizatu guztiak betiko ezabatuko dira."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bilatu lasterbideak"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Bai, berrezarri"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Utzi"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Sakatu tekla"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Tekla-konbinazio hori erabili da dagoeneko. Probatu beste konbinazio batekin."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Ezin da ezarri lasterbidea."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Gehitu lasterbide bat"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Egin atzera"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Joan orri nagusira"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ikusi azkenaldiko aplikazioak"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Aldatu aplikazioa"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Eginda"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Saiatu berriro!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Egin atzera"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bikain!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Osatu duzu azkenaldiko aplikazioak ikusteko keinua."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Azkenaldiko aplikazioak ikusteko, pasatu 3 hatz gora eta eduki sakatuta ukipen-panelean"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Aldatu aplikazioa"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bikain!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Ikasi duzu aplikazio batetik bestera aldatzeko keinua."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Ikusi aplikazio guztiak"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Sakatu teklatuko ekintza-tekla"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 9a06c8d487be..3612b16de761 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -167,7 +167,7 @@
<string name="issuerecord_start_error" msgid="3402782952722871190">"هنگام شروع ضبط مشکل، خطایی پیش آمد"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"درحال مشاهده در حالت تمام‌صفحه"</string>
<string name="immersive_cling_description" msgid="2717426731830851921">"برای خروج، از بالای صفحه تند به پایین بکشید"</string>
- <string name="immersive_cling_positive" msgid="3076681691468978568">"متوجه‌ام"</string>
+ <string name="immersive_cling_positive" msgid="3076681691468978568">"متوجهم"</string>
<string name="accessibility_back" msgid="6530104400086152611">"برگشت"</string>
<string name="accessibility_home" msgid="5430449841237966217">"صفحهٔ اصلی"</string>
<string name="accessibility_menu" msgid="2701163794470513040">"منو"</string>
@@ -534,7 +534,7 @@
<string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"افزایش ارتفاع"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ابزاره‌های صفحه قفل"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"برای باز کردن برنامه بااستفاده از ابزاره، باید هویت خودتان را به‌تأیید برسانید. همچنین، به‌خاطر داشته باشید که همه می‌توانند آن‌ها را مشاهده کنند، حتی وقتی رایانه لوحی‌تان قفل است. برخی‌از ابزاره‌ها ممکن است برای صفحه قفل درنظر گرفته نشده باشند و ممکن است اضافه کردن آن‌ها در اینجا ناامن باشد."</string>
- <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"متوجه‌ام"</string>
+ <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"متوجهم"</string>
<string name="glanceable_hub_lockscreen_affordance_label" msgid="1461611028615752141">"ابزاره‌ها"</string>
<string name="glanceable_hub_lockscreen_affordance_disabled_text" msgid="599170482297578735">"برای افزودن میان‌بر «ابزاره‌ها»، مطمئن شوید «نمایش ابزاره‌ها در صفحه قفل» در تنظیمات فعال باشد."</string>
<string name="glanceable_hub_lockscreen_affordance_action_button_label" msgid="7636151133344609375">"تنظیمات"</string>
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"درحال به‌روزرسانی"</string>
<string name="status_bar_work" msgid="5238641949837091056">"نمایه کاری"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"حالت هواپیما"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"در ساعت <xliff:g id="WHEN">%1$s</xliff:g>، دیگر صدای زنگ ساعت را نمی‌شنوید"</string>
<string name="alarm_template" msgid="2234991538018805736">"در <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"در <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"برنامه‌های سیستم"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"چندوظیفگی"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"صفحهٔ دونیمه"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ورودی"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"میان‌برهای برنامه"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"برنامه فعلی"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"سفارشی‌سازی میان‌برها"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"میان‌بر حذف شود؟"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"به تنظیم پیش‌فرض بازنشانی می‌کنید؟"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"برای ایجاد این میان‌بر، دکمه «کنش» و یک یا چند دکمه دیگر را هم‌زمان فشار دهید."</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"با این کار، میان‌بر سفارشی شما برای همیشه حذف می‌شود."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"با این کار، همه میان‌برهای سفارشی برای همیشه حذف خواهند شد."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"جستجوی میان‌برها"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"بله، بازنشانی شود"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"لغو"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"کلید را فشار دهید"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"ترکیب کلید ازقبل درحال استفاده است. ترکیب دیگری را امتحان کنید."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"میان‌بر تنظیم نشد."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"افزودن میان‌بر"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"برگشتن"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"رفتن به صفحه اصلی"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"مشاهده برنامه‌های اخیر"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"جابه‌جایی بین برنامه‌ها"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"تمام"</string>
<string name="gesture_error_title" msgid="469064941635578511">"دوباره امتحان کنید!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"برگشتن"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"عالی است!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"اشاره «مشاهده برنامه‌های اخیر» را تمام کردید"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"برای مشاهده برنامه‌های اخیر، با سه انگشت روی صفحه لمسی تند به‌بالا بکشید و نگه دارید"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"جابه‌جایی بین برنامه‌ها"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"عالی است!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"اشاره جابه‌جایی بین برنامه‌ها را تکمیل کردید."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"مشاهده همه برنامه‌ها"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"دکمه کنش را روی صفحه لمسی فشار دهید"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 4139472efd66..9f1c72306b22 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -752,6 +752,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Päivitetään"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Työprofiili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lentokonetila"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Et kuule seuraavaa hälytystäsi (<xliff:g id="WHEN">%1$s</xliff:g>)."</string>
<string name="alarm_template" msgid="2234991538018805736">"kello <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"ajankohtana <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1432,6 +1434,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Järjestelmäsovellukset"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitaskaus"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Jaettu näyttö"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Syöte"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Sovellusten pikakuvakkeet"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Nykyinen sovellus"</string>
@@ -1440,8 +1444,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Muokkaa pikanäppäimiä"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Poistetaanko pikanäppäin?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Palautetaanko oletusasetukset?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Voit luoda tämän pikanäppäinyhdistelmän painamalla samaan aikaan toimintonäppäintä ja yhtä tai useampaa muuta näppäintä"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Oma pikanäppäin poistetaan pysyvästi."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Kaikki omat pikanäppäimet poistetaan pysyvästi."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pikahaut"</string>
@@ -1463,8 +1466,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Kyllä, nollaa"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Peru"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Paina näppäintä"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Näppäinyhdistelmä on jo käytössä. Kokeile toista yhdistelmää."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Pikakuvaketta ei voi lisätä."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Lisää pikanäppäin"</string>
@@ -1478,8 +1480,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Takaisin"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Siirry etusivulle"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Katso viimeisimmät sovellukset"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Vaihda sovellusta"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Valmis"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Yritä uudelleen."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Takaisin"</string>
@@ -1497,15 +1498,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Hienoa!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Olet oppinut Katso viimeisimmät sovellukset ‑eleen."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Näet äskeiset sovellukset, kun pyyhkäiset ylös ja pidät kosketuslevyä painettuna kolmella sormella."</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Vaihda sovellusta"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Hienoa!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Olet oppinut sovelluksenvaihtoeleen."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Näytä kaikki sovellukset"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Paina näppäimistön toimintonäppäintä"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 5247b395a493..6bcce5300d38 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Mise à jour en cours…"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Vous n\'entendrez pas votre prochaine alarme à <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"à <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"le <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Applis système"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitâche"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Écran divisé"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrée"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Raccourcis des applis"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Appli actuelle"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Personnaliser les raccourcis"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Supprimer le raccourci?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Réinitialiser aux raccourcis par défaut?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Pour créer ce raccourci, appuyez simultanément sur la touche d\'action et une ou plusieurs autres touches"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Cela supprimera définitivement votre raccourci personnalisé."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Cette action supprimera définitivement tous vos raccourcis personnalisés."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Rechercher des raccourcis"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Oui, réinitialiser"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuler"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Appuyez sur la touche"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"La combinaison de touches est déjà utilisée. Essayez une autre combinaison."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Le raccourci ne peut pas être défini."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Ajouter un raccourci"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Retour"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Retour à la page d\'accueil"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Afficher les applis récentes"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Changer d\'appli"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"OK"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Réessayez!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Retour"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bon travail!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Vous avez effectué le geste pour afficher les applis récentes."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Pour afficher vos applis récentes, balayez votre pavé tactile vers le haut avec trois doigts et maintenez-les en place"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Changer d\'appli"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bon travail!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Vous avez terminé le geste de changement d\'appli."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Afficher toutes les applis"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 6fc2434b8d24..33628260ff49 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Mise à jour"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Vous n\'entendrez pas votre prochaine alarme <xliff:g id="WHEN">%1$s</xliff:g>."</string>
<string name="alarm_template" msgid="2234991538018805736">"à <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"le <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Applis système"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitâche"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Écran partagé"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Saisie"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Raccourcis d\'application"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Appli actuelle"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Personnaliser les raccourcis"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Supprimer le raccourci ?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Rétablir les paramètres par défaut ?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Pour créer ce raccourci, appuyez simultanément sur la touche d\'action et une ou plusieurs autres touches"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Votre raccourci personnalisé sera définitivement supprimé."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tous vos raccourcis personnalisés seront définitivement supprimés."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Rechercher des raccourcis"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Oui, rétablir"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuler"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Appuyez sur la touche"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Combinaison de touches déjà utilisée. Essayez une autre combinaison."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Impossible de définir le raccourci."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Ajouter un raccourci"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Retour"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Retour à l\'accueil"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Afficher les applis récentes"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Passer d\'une application à l\'autre"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"OK"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Essayez encore."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Retour"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bravo !"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Vous avez appris le geste pour afficher les applis récentes"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Pour afficher les applis récentes, balayez le pavé tactile vers le haut avec trois doigts et maintenez la position"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Passer d\'une application à l\'autre"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bravo !"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Vous avez appris le geste pour passer d\'une appli à l\'autre."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Afficher toutes les applications"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index f7ee84c1cc06..60fe0b098d01 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Actualizando"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de traballo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Non escoitarás a alarma seguinte <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"ás <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"o <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicacións do sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefa"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atallos de aplicacións"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicación actual"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Personalizar os atallos"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Queres quitar o atallo?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Queres restablecer a opción predeterminada?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Para crear este atallo, preme a tecla de acción e unha ou máis teclas á vez"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Eliminarase de forma permanente o teu atallo personalizado."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Eliminaranse permanentemente todos os teus atallos personalizados."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Busca atallos"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Si, restablecer"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Preme unha tecla"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Xa se está usando esta combinación de teclas. Proba con outra."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Non se puido definir o atallo."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Engadir un atallo"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Volver"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir ao inicio"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Consultar aplicacións recentes"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Cambiar de aplicación"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Feito"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Téntao de novo."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Volver"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Moi ben!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Completaches o titorial do xesto de consultar aplicacións recentes."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Para ver as aplicacións recentes, pasa tres dedos cara arriba e mantenos premidos no panel táctil"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Cambiar de aplicación"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bravo!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Completaches o xesto para cambiar de aplicación."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas as aplicacións"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Preme a tecla de acción do teclado"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index d243e1925dae..f9c5b81a615d 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"અપડેટ કરી રહ્યાં છીએ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ઑફિસની પ્રોફાઇલ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"એરપ્લેન મોડ"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"તમે <xliff:g id="WHEN">%1$s</xliff:g> એ તમારો આગલો એલાર્મ સાંભળશો નહીં"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> વાગ્યે"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> એ"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"સિસ્ટમ ઍપ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"એકથી વધુ કાર્યો કરવા"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"સ્ક્રીનને વિભાજિત કરો"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ઇનપુટ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ઍપ શૉર્ટકટ"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"હાલની ઍપ"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"શૉર્ટકટ કસ્ટમાઇઝ કરો"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"શું શૉર્ટકટ કાઢી નાખીએ?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"પાછા ડિફૉલ્ટ પર રીસેટ કરીએ?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"આ શૉર્ટકટ બનાવવા માટે, ઍક્શન કી અને એક કે તેથી વધુ કી એકસાથે દબાવો"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"આ તમારા કસ્ટમ શૉર્ટકટને કાયમી રીતે ડિલીટ કરશે."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"આ તમારા બધા કસ્ટમ શૉર્ટકટને કાયમ માટે ડિલીટ કરશે."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"શૉર્ટકટ શોધો"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"હા, રીસેટ કરો"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"રદ કરો"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"કી દબાવો"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"કી સંયોજન પહેલેથી ઉપયોગમાં છે. અન્ય સંયોજન અજમાવી જુઓ."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"શૉર્ટકટ સેટ કરી શકાતો નથી."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"શૉર્ટકટ ઉમેરો"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"પાછા જાઓ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"હોમ પર જાઓ"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"તાજેતરની ઍપ જુઓ"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"ઍપ સ્વિચ કરો"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"થઈ ગયું"</string>
<string name="gesture_error_title" msgid="469064941635578511">"ફરી પ્રયાસ કરો!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"પાછા જાઓ"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ખૂબ સરસ કામ!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"તમે \'તાજેતરની ઍપ જુઓ\' સંકેત પૂર્ણ કર્યો."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"તાજેતરની ઍપ જોવા માટે, તમારા ટચપૅડ પર ત્રણ આંગળી વડે ઉપરની તરફ સ્વાઇપ કરો અને દબાવી રાખો"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ઍપ સ્વિચ કરો"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"ખૂબ સરસ કામ!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"તમે ઍપ સ્વિચ કરવાનો સંકેત પૂર્ણ કર્યો છે."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"બધી ઍપ જુઓ"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"તમારા કીબોર્ડ પરની ઍક્શન કી દબાવો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 271ba1652c4b..efb080ab0204 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"अपडेट हो रहा है"</string>
<string name="status_bar_work" msgid="5238641949837091056">"वर्क प्रोफ़ाइल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"फ़्लाइट मोड"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"आपको <xliff:g id="WHEN">%1$s</xliff:g> पर अपना अगला अलार्म नहीं सुनाई देगा"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> बजे"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> पर"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"सिस्टम के ऐप्लिकेशन"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"मल्टीटास्किंग (एक साथ कई काम करना)"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"स्प्लिट स्क्रीन"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"इनपुट"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ऐप शॉर्टकट"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"मौजूदा ऐप्लिकेशन"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"पसंद के मुताबिक शॉर्टकट बनाएं"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"क्या आपको शॉर्टकट हटाना है?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"क्या आपको फिर से डिफ़ॉल्ट सेटिंग चालू करनी है?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"यह शॉर्टकट बनाने के लिए, ऐक्शन बटन और एक या उससे ज़्यादा अन्य बटन एक साथ दबाएं"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ऐसा करने पर, पसंद के मुताबिक बनाया गया आपका शॉर्टकट हमेशा के लिए मिट जाएगा."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ऐसा करने पर, पसंद के मुताबिक बनाए गए आपके सभी शॉर्टकट हमेशा के लिए मिट जाएंगे."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शॉर्टकट खोजें"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"हां, रीसेट करें"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"रद्द करें"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"बटन दबाएं"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"बटन का यह कॉम्बिनेशन पहले से इस्तेमाल किया जा रहा है. कोई दूसरा कॉम्बिनेशन आज़माएं."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"शॉर्टकट सेट नहीं किया जा सकता."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"शॉर्टकट जोड़ें"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"वापस जाएं"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"होम स्क्रीन पर जाएं"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखें"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"ऐप्लिकेशन के बीच स्विच करें"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"हो गया"</string>
<string name="gesture_error_title" msgid="469064941635578511">"फिर से कोशिश करें!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"वापस जाएं"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"बहुत बढ़िया!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"अब आपको हाथ के जेस्चर का इस्तेमाल करके, हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखने का तरीका पता चल गया है."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखने के लिए, अपने टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करें और दबाकर रखें"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ऐप्लिकेशन के बीच स्विच करें"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"बहुत बढ़िया!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"आपने जान लिया है कि हाथ का जेस्चर इस्तेमाल करके ऐप्लिकेशन के बीच स्विच कैसे करें."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"सभी ऐप्लिकेशन देखें"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"अपने कीबोर्ड पर ऐक्शन बटन दबाएं"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 345df1940ded..5078bfffeff9 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ažuriranje"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Poslovni profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način rada u zrakoplovu"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sljedeći alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikacije sustava"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Obavljanje više zadataka"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podijeljeni zaslon"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečaci aplikacija"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutačna aplikacija"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Natrag"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Na početni zaslon"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Pregled nedavnih aplikacija"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Promjena aplikacije"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Pokušajte ponovno!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Natrag"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Sjajno!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Napravili ste pokret za prikaz nedavno korištenih aplikacija."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Za prikaz nedavnih aplikacija prijeđite trima prstima prema gore na dodirnoj podlozi i zadržite pritisak"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Promjena aplikacije"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Sjajno!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Izvršili ste pokret za promjenu aplikacije."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Prikaži sve aplikacije"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipku za radnju na tipkovnici"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 80f4d24aa652..cce273260779 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Frissítés…"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Munkahelyi profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Repülős üzemmód"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nem fogja hallani az ébresztést ekkor: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"ekkor: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"ezen a napon: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Rendszeralkalmazások"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Osztott képernyő"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Bevitel"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Alkalmazásikonok"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Jelenlegi alkalmazás"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Gyorsparancsok személyre szabása"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Eltávolítja a billentyűparancsot?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Visszaállítja az alapértelmezett beállításokat?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"A billentyűparancs létrehozásához nyomja le egyszerre a műveletbillentyűt és egy vagy több másik billentyűt"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ezzel véglegesen törli az egyéni billentyűparancsot."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Ezzel véglegesen törli az összes egyéni billentyűparancsot."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Billentyűparancsok keresése"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Igen, visszaállítom"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Mégse"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nyomja le a billentyűt"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"A billentyűkombináció már használatban van. Próbálkozzon másik kombinációval."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Nem lehet beállítani a billentyűparancsot."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Billentyűparancs hozzáadása"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Vissza"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ugrás a főoldalra"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Legutóbbi alkalmazások megtekintése"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Váltás az alkalmazások között"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Kész"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Próbálja újra"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Vissza"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Kiváló!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Teljesítette a legutóbbi alkalmazások megtekintésének kézmozdulatát."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"A legutóbbi appokért csúsztasson gyorsan három ujjal felfelé az érintőpadon, és tartsa lenyomva ujjait."</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Váltás az alkalmazások között"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Kiváló!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Teljesítette az appváltó gesztust."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Összes alkalmazás megtekintése"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Nyomja meg a műveletbillentyűt az érintőpadon."</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 5e17aedba083..10cf6703e3e0 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Թարմացում"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Android for Work-ի պրոֆիլ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Ավիառեժիմ"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ժամը <xliff:g id="WHEN">%1$s</xliff:g>-ի զարթուցիչը չի զանգի"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>-ին"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>-ին"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Համակարգային հավելվածներ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Բազմա­խնդրու­թ­յուն"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Տրոհված էկրան"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ներածում"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Հավելվածի դյուրանցումներ"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Այս հավելվածը"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Դյուրանցումների անհատականացում"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Հեռացնե՞լ դյուրանցումը"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Վերականգնե՞լ կանխադրվածները"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Այս դյուրանցումը ստեղծելու համար սեղմեք գործողության ստեղնը ու ևս մեկ կամ մի քանի ստեղներ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ձեր հատուկ դյուրանցումն ընդմիշտ կջնջվի։"</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Բոլոր հատուկ դյուրանցումներն ընդմիշտ կջնջվեն։"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Դյուրանցումների որոնում"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Վերականգնել"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Չեղարկել"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Սեղմեք որևէ ստեղն"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Ստեղների համակցությունն արդեն օգտագործվում է։ Ընտրեք ուրիշը։"</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Դյուրանցումը հնարավոր չէ ստեղծել։"</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Ավելացնել դյուրանցում"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Ինչպես հետ գնալ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ինչպես անցնել հիմնական էկրան"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Դիտել վերջին հավելվածները"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Անցում մեկ հավելվածից մյուսին"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Պատրաստ է"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Նորից փորձեք։"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Հետ գնալ"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Կեցցե՛ք"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Դուք կատարեցիք վերջին օգտագործված հավելվածների դիտման ժեստը։"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Վերջին հավելվածները տեսնելու համար երեք մատը սահեցրեք վերև և սեղմած պահեք հպահարթակին"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Անցում մեկ հավելվածից մյուսին"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Կեցցե՛ք։"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Դուք սովորեցիք ուրիշ հավելված անցնելու ժեստը։"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Ինչպես դիտել բոլոր հավելվածները"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Սեղմեք գործողության ստեղնը ստեղնաշարի վրա"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index c848527591c6..a5f01997654a 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Update berlangsung"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode pesawat"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Anda tidak akan mendengar alarm berikutnya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"pukul <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"pada <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikasi sistem"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Layar terpisah"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Pintasan aplikasi"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplikasi Saat Ini"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Sesuaikan pintasan"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Hapus pintasan?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset kembali ke pintasan default?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Untuk membuat pintasan ini, tekan tombol Tindakan dan satu atau beberapa tombol lainnya secara bersamaan"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Tindakan ini akan menghapus pintasan kustom Anda secara permanen."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tindakan ini akan menghapus semua pintasan kustom Anda secara permanen."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Telusuri pintasan"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ya, reset"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Batal"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tekan tombol"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Kombinasi tombol sudah digunakan. Coba kombinasi lain."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Pintasan tidak dapat disetel."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Tambahkan pintasan"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Kembali"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Buka layar utama"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Lihat aplikasi terbaru"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Beralih aplikasi"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Selesai"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Coba lagi"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kembali"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bagus!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Anda telah menyelesaikan gestur untuk melihat aplikasi terbaru."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Untuk melihat aplikasi terbaru, geser ke atas dan tahan menggunakan tiga jari di touchpad"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Beralih aplikasi"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bagus!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Anda telah menyelesaikan gestur beralih aplikasi."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Lihat semua aplikasi"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tekan tombol tindakan di keyboard"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 6bfaab975f58..576a22742573 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Uppfærir"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Vinnusnið"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flugstilling"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ekki mun heyrast í vekjaranum <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Kerfisforrit"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Fjölvinnsla"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Skjáskipting"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Innsláttur"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Flýtileiðir forrita"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Núverandi forrit"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Sérsníða flýtilykla"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Fjarlægja flýtileið?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Endurstilla á sjálfgefið?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Til að búa til þessa flýtileið skaltu ýta á aðgerðalykilinn og einn eða fleiri lykla saman"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Þetta eyðir sérsniðnu flýtileiðinni varanlega."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Þetta verður til þess að öllum sérsniðnu flýtileiðunum þínum verður eytt varanlega."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leita að flýtileiðum"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Já, endurstilla"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Hætta við"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Ýttu á lykil"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Lyklasamsetningin er þegar í notkun. Prófaðu aðra samsetningu."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Ekki er hægt að stilla flýtileið."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Bæta flýtileið við"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Til baka"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Fara á heimaskjá"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Sjá nýleg forrit"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Að skipta á milli forrita"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Lokið"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Reyndu aftur!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Til baka"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Vel gert!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Þú framkvæmdir bendinguna til að sjá nýleg forrit."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Strjúktu upp og haltu þremur fingrum inni á snertifletinum til að sjá nýleg forrit"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Að skipta á milli forrita"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Vel gert!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Þú framkvæmdir bendinguna til að skipta á milli forrita."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Sjá öll forrit"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Ýttu á aðgerðalykilinn á lyklaborðinu"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 299107784637..9b847c4c344d 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Aggiornamento in corso…"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profilo di lavoro"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modalità aereo"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Non sentirai la tua prossima sveglia <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"alle <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"App di sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Schermo diviso"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Scorciatoie app"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App corrente"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Personalizza scorciatoie"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Rimuovere scorciatoia?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vuoi ripristinare le impostazioni predefinite?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Per creare questa scorciatoia, premi il tasto Azione e uno o più altri tasti contemporaneamente"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"La scorciatoia personalizzata verrà eliminata definitivamente."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tutte le tue scorciatoie personalizzate verranno eliminate definitivamente."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Cerca scorciatoie"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sì, ripristina"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annulla"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Premi un tasto"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Combinazione di tasti già in uso. Prova un\'altra combinazione."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Impossibile impostare la scorciatoia."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Aggiungi scorciatoia"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Indietro"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Vai alla schermata Home"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Visualizza app recenti"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Cambia app"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fine"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Riprova."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Indietro"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Ottimo lavoro!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Hai completato il gesto Visualizza app recenti."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Per visualizzare le app recenti, scorri verso l\'alto e tieni premuto con tre dita sul touchpad"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Cambia app"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Ottimo lavoro."</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Hai completato il gesto Cambia app."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Visualizza tutte le app"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Premi il tasto azione sulla tastiera"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 4a0469464555..afa1af584c44 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"מתבצע עדכון"</string>
<string name="status_bar_work" msgid="5238641949837091056">"פרופיל עבודה"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"מצב טיסה"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"לא ניתן יהיה לשמוע את השעון המעורר הבא שלך <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"בשעה <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"ב-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"אפליקציות מערכת"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ריבוי משימות"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"מסך מפוצל"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"קלט"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"מקשי קיצור לאפליקציות"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"האפליקציה הנוכחית"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"התאמה אישית של מקשי הקיצור"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"להסיר את קיצור הדרך?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"לאפס לברירת המחדל?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"כדי ליצור את קיצור הדרך הזה, מקישים על מקש הפעולה ועל עוד מקש אחד או יותר ביחד"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"קיצור הדרך יימחק לתמיד."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"הפעולה הזו תמחק לתמיד את כל קיצורי הדרך שמותאמים אישית."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"חיפוש מקשי קיצור"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"כן, לאפס"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ביטול"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"יש ללחוץ על מקש"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"שילוב המקשים הזה תפוס. צריך לנסות שילוב אחר."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"לא ניתן להגדיר את קיצור הדרך."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"הוספת קיצור דרך"</string>
@@ -1497,13 +1499,13 @@
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"כדי לראות את האפליקציות האחרונות, צריך להחליק למעלה וללחוץ לחיצה ארוכה עם שלוש אצבעות על לוח המגע"</string>
<!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
<skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
<!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
<skip />
<!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"צפייה בכל האפליקציות"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"צריך להקיש על מקש הפעולה במקלדת"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index e764514b6a43..87c6aedfd4a0 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"更新しています"</string>
<string name="status_bar_work" msgid="5238641949837091056">"仕事用プロファイル"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"機内モード"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"次回のアラーム(<xliff:g id="WHEN">%1$s</xliff:g>)は鳴りません"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"システムアプリ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"マルチタスク"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分割画面"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"入力"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"アプリのショートカット"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"現在のアプリ"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"ショートカットのカスタマイズ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ショートカットを削除しますか?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"デフォルトにリセットしますか?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"このショートカットを作成するには、アクションキーと他のキー(複数可)を同時に押してください"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"この操作を行うと、カスタム ショートカットが完全に削除されます。"</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"この操作を行うと、すべてのカスタム ショートカットが完全に削除されます。"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ショートカットの検索"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"リセット"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"キャンセル"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"キーを押してください"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"このキーの組み合わせはすでに使用されています。別の組み合わせをお試しください。"</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"ショートカットを設定できません。"</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"ショートカットを追加"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"戻る"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ホームに移動"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"最近使ったアプリを表示する"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"アプリの切り替え"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完了"</string>
<string name="gesture_error_title" msgid="469064941635578511">"もう一度お試しください。"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"戻る"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"よくできました!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"「最近使ったアプリを表示する」ジェスチャーを学習しました。"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"最近使ったアプリを表示するには、3 本の指でタッチパッドを上にスワイプして長押しします"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"アプリの切り替え"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"よくできました!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"アプリを切り替えるジェスチャーを学習しました。"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"すべてのアプリを表示"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"キーボードのアクションキーを押します"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 0e790505a6bd..31f486f05373 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"მიმდინარეობს განახლება"</string>
<string name="status_bar_work" msgid="5238641949837091056">"სამსახურის პროფილი"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"თვითმფრინავის რეჟიმი"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"ვერ გაიგონებთ მომდევნო მაღვიძარას <xliff:g id="WHEN">%1$s</xliff:g>-ზე"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>-ზე"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>-ზე"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"სისტემის აპები"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"მრავალამოცანიანი რეჟიმი"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ეკრანის გაყოფა"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"შეყვანა"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"აპის მალსახმობები"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"მიმდინარე აპი"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"უკან დაბრუნება"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"მთავარ ეკრანზე გადასვლა"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ბოლო აპების ნახვა"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"აპების გადართვა"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"მზადაა"</string>
<string name="gesture_error_title" msgid="469064941635578511">"ცადეთ ხელახლა!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"უკან დაბრუნება"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"შესანიშნავია!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"თქვენ დაასრულეთ ბოლო აპების ხედის ჟესტი."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"ბოლო აპების სანახავად თქვენს სენსორულ პანელზე სამი თითით გადაფურცლეთ ზევით და ხანგრძლივად დააჭირეთ"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"აპების გადართვა"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"შესანიშნავია!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"თქვენ შეასრულეთ აპების გადართვის ჟესტი."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"ყველა აპის ნახვა"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"დააჭირეთ მოქმედების კლავიშს თქვენს კლავიატურაზე"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index f7369d2cd5be..0b6c7b9919b2 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Жаңартылып жатыр"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Жұмыс профилі"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Ұшақ режимі"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Келесі <xliff:g id="WHEN">%1$s</xliff:g> дабылыңызды есітпейсіз"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Жүйелік қолданбалар"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Мультитаскинг"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Экранды бөлу"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Енгізу"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Қолданба таңбашалары"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Қолданыстағы қолданба"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Пернелер тіркесімін бейімдеу"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Тіркесімді өшіру керек пе?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Әдепкі тіркесімге қайтару керек пе?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Осы тіркесімді жасау үшін әрекет пернесін және басқа бір не бірнеше пернені бірге басыңыз."</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Арнаулы тіркесіміңіз біржола жойылады."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Мұндайда барлық арнаулы тіркесім біржола жойылады."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Тіркесімдерді іздеу"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Иә, қайтару"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Бас тарту"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Пернені басыңыз"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Бұл пернелер тіркесімі бұрыннан қолданылады. Басқа тіркесімді пайдаланып көріңіз."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Перне тіркесімін орнату мүмкін емес."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Жылдам пәрмен қосу"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Артқа"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Негізгі бетке өту"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Соңғы қолданбаларды көру"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Қолданба ауыстыру"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Дайын"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Қайталап көріңіз"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Артқа"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Жарайсыз!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Соңғы қолданбаларды көру қимылын орындадыңыз."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Соңғы қолданбаларды көру үшін сенсорлық тақтада үш саусақпен жоғары сырғытып, ұстап тұрыңыз."</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Қолданба ауыстыру"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Жарайсыз!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Қолданба ауыстыру қимылын аяқтадыңыз."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Барлық қолданбаны көру"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Пернетақтадағы әрекет пернесін басыңыз."</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 3b07faeef89d..a2f5b01c81de 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"កំពុងដំឡើង​កំណែ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"កម្រងព័ត៌មានការងារ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ពេលជិះយន្តហោះ"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"អ្នកនឹងមិនលឺម៉ោងរោទ៍ <xliff:g id="WHEN">%1$s</xliff:g> បន្ទាប់របស់អ្នកទេ"</string>
<string name="alarm_template" msgid="2234991538018805736">"នៅ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"នៅ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"កម្មវិធី​ប្រព័ន្ធ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ការធ្វើកិច្ចការច្រើន"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"មុខងារ​បំបែកអេក្រង់"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"វិធីបញ្ចូល"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ផ្លូវកាត់​កម្មវិធី"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"កម្មវិធីបច្ចុប្បន្ន"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"ប្ដូរ​ផ្លូវ​កាត់តាម​បំណង"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ដក​ផ្លូវកាត់​ចេញឬ?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"កំណត់ឡើងវិញទៅលំនាំដើមឬ?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"ដើម្បីបង្កើតផ្លូវកាត់នេះ សូមចុចគ្រាប់ចុចសកម្មភាព និងគ្រាប់ចុចមួយ ឬច្រើនផ្សេងទៀតជាមួយគ្នា"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ការធ្វើបែបនេះនឹងលុបផ្លូវ​កាត់ផ្ទាល់ខ្លួនរបស់អ្នកជាអចិន្ត្រៃយ៍។"</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"សកម្មភាពនេះនឹងលុបផ្លូវកាត់ផ្ទាល់ខ្លួនរបស់អ្នកទាំងអស់ជាអចិន្ត្រៃយ៍។"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ស្វែងរកផ្លូវ​កាត់"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"បាទ/ចាស កំណត់ឡើងវិញ"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"បោះបង់"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ចុចគ្រាប់ចុច"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"កំពុងប្រើបន្សំគ្រាប់ចុចស្រាប់ហើយ។ សូមសាកល្បងបន្សំគ្រាប់ចុចផ្សេង។"</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"មិនអាចកំណត់ផ្លូវកាត់បានទេ។"</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"បញ្ចូល​ផ្លូវកាត់"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ថយ​ក្រោយ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ទៅទំព័រដើម"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"មើលកម្មវិធីថ្មីៗ"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"ប្ដូរ​កម្មវិធី"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"រួចរាល់"</string>
<string name="gesture_error_title" msgid="469064941635578511">"សូមព្យាយាមម្ដងទៀត!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ថយ​ក្រោយ"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ធ្វើបានល្អ!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"អ្នកបានបញ្ចប់ការមើលចលនាកម្មវិធីថ្មីៗ។"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"ដើម្បីមើលកម្មវិធីថ្មីៗ សូមអូសឡើងលើ រួចសង្កត់ឱ្យជាប់លើផ្ទាំងប៉ះរបស់អ្នក ដោយប្រើម្រាមដៃបី"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ប្ដូរ​កម្មវិធី"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"ធ្វើបានល្អ!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"អ្នក​បានបញ្ចប់​ចលនា​ប្ដូរកម្មវិធី​ហើយ។"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"មើល​កម្មវិធី​ទាំងអស់"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ចុចគ្រាប់ចុចសកម្មភាពលើក្ដារចុចរបស់អ្នក"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 544c81040ce9..76f38c0337db 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"ಅಪ್‌ಡೇಟ್‌‌ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"ನಿಮ್ಮ ಮುಂದಿನ <xliff:g id="WHEN">%1$s</xliff:g> ಅಲಾರಮ್ ಅನ್ನು ನೀವು ಆಲಿಸುವುದಿಲ್ಲ"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ರಲ್ಲಿ"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> ರಂದು"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ಸಿಸ್ಟಂ ಆ್ಯಪ್‌ಗಳು"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ಮಲ್ಟಿಟಾಸ್ಕಿಂಗ್"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ಇನ್‌ಪುಟ್"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ಆ್ಯಪ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ಪ್ರಸ್ತುತ ಆ್ಯಪ್"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ಶಾರ್ಟ್‌ಕಟ್ ಅನ್ನು ತೆಗೆದುಹಾಕಬೇಕೇ?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ಡೀಫಾಲ್ಟ್‌ಗೆ ರೀಸೆಟ್ ಮಾಡಬೇಕೆ?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"ಈ ಶಾರ್ಟ್‌ಕಟ್ ಅನ್ನು ರಚಿಸಲು, ಆಕ್ಷನ್ ಕೀ ಮತ್ತು ಒಂದು ಅಥವಾ ಹೆಚ್ಚಿನ ಇತರ ಕೀಗಳನ್ನು ಒಟ್ಟಿಗೆ ಒತ್ತಿರಿ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ಇದು ನಿಮ್ಮ ಕಸ್ಟಮ್ ಶಾರ್ಟ್‌ಕಟ್ ಅನ್ನು ಶಾಶ್ವತವಾಗಿ ಅಳಿಸುತ್ತದೆ."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ಇದು ನಿಮ್ಮ ಎಲ್ಲಾ ಕಸ್ಟಮ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಶಾಶ್ವತವಾಗಿ ಅಳಿಸುತ್ತದೆ."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ಹುಡುಕಾಟದ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ಹೌದು, ರೀಸೆಟ್ ಮಾಡಿ"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ರದ್ದುಮಾಡಿ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ಕೀ ಅನ್ನು ಒತ್ತಿರಿ"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"ಕೀ ಸಂಯೋಜನೆಯು ಈಗಾಗಲೇ ಬಳಕೆಯಲ್ಲಿದೆ. ಮತ್ತೊಂದು ಸಂಯೋಜನೆಯನ್ನು ನೋಡಿ."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"ಶಾರ್ಟ್‌ಕಟ್‌ ಅನ್ನು ಸೆಟ್‌ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"ಶಾರ್ಟ್‌ಕಟ್ ಸೇರಿಸಿ"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ಹಿಂದಿರುಗಿ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ಮುಖಪುಟಕ್ಕೆ ಹೋಗಿ"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"ಆ್ಯಪ್‌ಗಳನ್ನು ಬದಲಿಸಿ"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ಮುಗಿದಿದೆ"</string>
<string name="gesture_error_title" msgid="469064941635578511">"ಪುನಃ ಪ್ರಯತ್ನಿಸಿ!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ಹಿಂತಿರುಗಿ"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ಭೇಷ್‌!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ನೀವು ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳ ಜೆಸ್ಚರ್‌ ವೀಕ್ಷಣೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಲು, ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ಆ್ಯಪ್‌ಗಳನ್ನು ಬದಲಿಸಿ"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"ಭೇಷ್!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"ನೀವು ಆ್ಯಪ್‌ಗಳನ್ನು ಬದಲಾಯಿಸುವ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್‌ನಲ್ಲಿ ಆ್ಯಕ್ಷನ್‌ ಕೀಯನ್ನು ಒತ್ತಿ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 759a2aa85ef3..c087e5891229 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"업데이트 중"</string>
<string name="status_bar_work" msgid="5238641949837091056">"직장 프로필"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"비행기 모드"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>에 다음 알람을 들을 수 없습니다."</string>
<string name="alarm_template" msgid="2234991538018805736">"시간: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"일시: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"시스템 앱"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"멀티태스킹"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"화면 분할"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"입력"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"앱 단축키"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"현재 앱"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"단축키 맞춤설정"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"단축키를 삭제하시겠어요?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"기본값으로 재설정하시겠어요?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"이 단축키를 만들려면 작업 키와 다른 키 하나 이상을 함께 누르세요"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"맞춤 단축키가 영구적으로 삭제됩니다."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"모든 맞춤 단축키가 완전히 삭제됩니다."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"단축키 검색"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"재설정"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"취소"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"키를 누르세요."</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"이미 사용 중인 키 조합입니다. 다른 조합을 사용해 보세요."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"단축키를 설정할 수 없습니다."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"단축키 추가"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"뒤로 이동"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"홈으로 이동"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"최근 앱 보기"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"앱 전환"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"완료"</string>
<string name="gesture_error_title" msgid="469064941635578511">"다시 시도해 보세요"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"뒤로"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"아주 좋습니다"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"최근 앱 보기 동작을 완료했습니다."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"최근 앱을 보려면 터치패드에서 세 손가락을 사용해 위로 스와이프한 채로 유지하세요."</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"앱 전환"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"잘하셨습니다"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"앱 전환 동작을 완료했습니다."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"모든 앱 보기"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"키보드의 작업 키를 누르세요."</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index c1427567fe3b..5d1b604fa152 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Жаңырууда"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Жумуш профили"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Учак режими"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> боло турган кийинки эскертмени укпайсыз"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> болгондо"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> болгондо"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системанын колдонмолору"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Бир нече тапшырма аткаруу"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Экранды бөлүү"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Киргизүү"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Колдонмонун ыкчам баскычтары"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Учурдагы колдонмо"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Ыкчам баскычтарды тууралоо"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Ыкчам баскыч өчүрүлсүнбү?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Баштапкы абалга келтиресизби?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Бул ыкчам баскычты түзүү үчүн Аракет баскычын жана бир же бир нече башка баскычты чогуу басыңыз"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ушуну менен жеке ыкчам баскычыңыз биротоло өчүрүлөт."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Ушуну менен жеке ыкчам баскычтарыңыздын баары биротоло өчүрүлөт."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ыкчам баскычтарды издөө"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ооба, баштапкы абалга келтирилсин"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Баскычты басыңыз"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Ачкычтардын айкалышы колдонулууда. Башка айкалышты колдонуп көрүңүз."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Ыкчам баскычты коюу мүмкүн эмес."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Ыкчам баскыч кошуу"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Артка кайтуу"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Башкы бетке өтүү"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Акыркы колдонмолорду көрүү"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Колдонмолорду которуштуруу"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Бүттү"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Кайталап көрүңүз!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Артка кайтуу"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Азаматсыз!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Акыркы колдонмолорду көрүү жаңсоосун аткардыңыз."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Соңку колдонмолорду көрүү үчүн сенсордук тактаны үч манжаңыз менен жогору сүрүп, кармап туруңуз"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Колдонмолорду которуштуруу"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Азаматсыз!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"\"Башка колдонмого которулуу жаңсоосу боюнча үйрөткүчтү бүтүрдүңүз."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Бардык колдонмолорду көрүү"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Баскычтобуңуздагы аракет баскычын басыңыз"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 26e70af28bee..37713b32ac9d 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"ກຳລັງອັບເດດ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"​ໂປຣ​ໄຟລ໌​ບ່ອນ​ເຮັດ​ວຽກ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ໂໝດຢູ່ໃນຍົນ"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"ທ່ານ​ຈະ​ບໍ່​ໄດ້​ຍິນ​ສຽງ​ໂມງ​ປ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"ເວ​ລາ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"ວັນ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ແອັບລະບົບ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ການເຮັດຫຼາຍໜ້າວຽກພ້ອມກັນ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ແບ່ງໜ້າຈໍ"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ອິນພຸດ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ທາງລັດແອັບ"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ແອັບປັດຈຸບັນ"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ກັບຄືນ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ໄປຫາໜ້າຫຼັກ"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ເບິ່ງແອັບຫຼ້າສຸດ"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"ສະຫຼັບແອັບ"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ແລ້ວໆ"</string>
<string name="gesture_error_title" msgid="469064941635578511">"ກະລຸນາລອງໃໝ່!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ກັບຄືນ"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ດີຫຼາຍ!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ທ່ານເບິ່ງທ່າທາງຂອງແອັບຫຼ້າສຸດສຳເລັດແລ້ວ."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"ເພື່ອເບິ່ງແອັບຫຼ້າສຸດ, ໃຫ້ປັດຂຶ້ນແລ້ວຄ້າງໄວ້ໂດຍໃຊ້ສາມນິ້ວເທິງແຜ່ນສຳຜັດຂອງທ່ານ"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ສະຫຼັບແອັບ"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"ດີຫຼາຍ!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"ທ່ານເຮັດທ່າທາງສະຫຼັບແອັບສຳເລັດແລ້ວ."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"ເບິ່ງແອັບທັງໝົດ"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ກົດປຸ່ມຄຳສັ່ງຢູ່ແປ້ນພິມຂອງທ່ານ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index d8de5c0f8997..4292574de55e 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Atnaujinama"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Darbo profilis"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lėktuvo režimas"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Negirdėsite kito signalo <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemos programos"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Kelių užduočių atlikimas"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Išskaidyto ekrano režimas"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Įvestis"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Programos spartieji klavišai"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Esama programa"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Grįžti"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Eikite į pagrindinį puslapį"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Peržiūrėti naujausias programas"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Perjungti programas"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Atlikta"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Bandykite dar kartą!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Grįžti"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Puiku!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Atlikote naujausių programų peržiūros gestą."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Peržiūrėkite naujausias programas, jutiklinėje dalyje perbraukę aukštyn trimis pirštais ir palaikę"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Perjungti programas"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Puiku!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Atlikote programų perjungimo gestą."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Žr. visas programas"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Paspauskite klaviatūros veiksmų klavišą"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 4cce539f73df..cac42758f8bf 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Notiek atjaunināšana"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Darba profils"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lidojuma režīms"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nākamais signāls (<xliff:g id="WHEN">%1$s</xliff:g>) netiks atskaņots."</string>
<string name="alarm_template" msgid="2234991538018805736">"plkst. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistēmas lietotnes"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Vairākuzdevumu režīms"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekrāna sadalīšana"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ievade"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Lietotņu saīsnes"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Pašreizējā lietotne"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Īsinājumtaustiņu pielāgošana"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vai noņemt saīsni?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vai atiestatīt noklusējuma vērtības?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Lai izveidotu šo īsinājumtaustiņu, vienlaikus nospiediet darbību taustiņu un vienu vai vairākus citus taustiņus"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Veicot šo darbību, jūsu pielāgotā saīsne tiks neatgriezeniski izdzēsta."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Veicot šo darbību, visas jūsu pielāgotās saīsnes tiks neatgriezeniski dzēstas."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēt saīsnes"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Jā, atiestatīt"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Atcelt"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nospiediet taustiņu"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Taustiņu kombinācija jau tiek izmantota. Izmēģiniet citu kombināciju."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Nevar iestatīt saīsni."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Pievienot saīsni"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Atpakaļ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Pāriet uz sākuma ekrānu"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Skatīt nesen izmantotās lietotnes"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Pārslēgties starp lietotnēm"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gatavs"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Mēģiniet vēlreiz."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atpakaļ"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Lieliski!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Jūs sekmīgi veicāt nesen izmantoto lietotņu skatīšanas žestu."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Lai skatītu nesenās lietotnes, skārienpaliktnī ar trīs pirkstiem velciet augšup un turiet"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Pārslēgšanās starp lietotnēm"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Lieliski!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Jūs sekmīgi veicāt pārslēgšanās starp lietotnēm žestu."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Skatīt visas lietotnes"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tastatūrā nospiediet darbību taustiņu."</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index d6a5181582fe..b53cec3636e3 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Се ажурира"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Работен профил"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Авионски режим"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Нема да го слушнете следниот аларм <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"во <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"во <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системски апликации"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Мултитаскинг"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Поделен екран"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Внесување"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Кратенки за апликации"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Тековна апликација"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Приспособете ги кратенките"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Да се отстрани кратенката?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Да се ресетира на стандардните поставки?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"За да ја создадете кратенкава, притиснете го копчето за дејство и едно или повеќе други копчиња заедно"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ова ќе ја избрише вашата приспособена кратенка трајно."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Ова ќе ги избрише трајно сите ваши приспособени кратенки."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пребарувајте кратенки"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Да, ресетирај"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Откажи"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Притиснете копче"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Комбинацијата на копчиња веќе се користи. Обидете се со друга комбинација."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Кратенката не може да се постави."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Додај кратенка"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Врати се назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Оди на почетниот екран"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прикажи ги неодамнешните апликации"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Сменете ги апликациите"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Обидете се повторно!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Одлично!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Го завршивте движењето за прегледување на неодамнешните апликации."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Повлечете нагоре со три прста на допирната подлога и задржете за да ги видите неодамнешните апликации"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Сменете ги апликациите"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Одлично!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Го завршивте движењето за менување апликации."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Прегледајте ги сите апликации"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Притиснете го копчето за дејство на тастатурата"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 20a7729da105..48ec2c05e95b 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"അപ്ഡേറ്റ് ചെയ്യുന്നു"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ഫ്ലൈറ്റ് മോഡ്"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>-നുള്ള നിങ്ങളുടെ അടുത്ത അലാറം കേൾക്കില്ല"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>-ന്"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>-ന്"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"സിസ്‌റ്റം ആപ്പുകൾ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"മൾട്ടിടാസ്‌കിംഗ്"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"സ്‌ക്രീൻ വിഭജന മോഡ്"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ഇൻപുട്ട്"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ആപ്പ് കുറുക്കുവഴികൾ"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"നിലവിലെ ആപ്പ്"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"കുറുക്കുവഴികൾ ഇഷ്ടാനുസൃതമാക്കുക"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"കുറുക്കുവഴി നീക്കം ചെയ്യണോ?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ഡിഫോൾട്ടിലേക്ക് തിരികെ റീസെറ്റ് ചെയ്യണോ?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"ഈ കുറുക്കുവഴി സൃഷ്‌ടിക്കാൻ, ആക്ഷൻ കീയും ഒന്നോ അതിലധികമോ മറ്റ് കീകളും ഒരുമിച്ച് അമർത്തുക"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ഇത് നിങ്ങളുടെ ഇഷ്‌ടാനുസൃത കുറുക്കുവഴി ശാശ്വതമായി ഇല്ലാതാക്കും."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"നിങ്ങളുടെ എല്ലാ ഇഷ്‍ടാനുസൃത കുറുക്കുവഴികളും ശാശ്വതമായി ഇത് ഇല്ലാതാക്കും."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"തിരയൽ കുറുക്കുവഴികൾ"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ഉവ്വ്, റീസെറ്റ് ചെയ്യുക"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"റദ്ദാക്കുക"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"കീ അമർത്തുക"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"കീ കോമ്പിനേഷൻ ഇതിനകം ഉപയോഗത്തിലുണ്ട്. മറ്റൊരു കോമ്പിനേഷൻ ശ്രമിക്കുക."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"കുറുക്കുവഴി സജ്ജീകരിക്കാനാകില്ല."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"കുറുക്കുവഴി ചേർക്കുക"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"മടങ്ങുക"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ഹോമിലേക്ക് പോകുക"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"അടുത്തിടെയുള്ള ആപ്പുകൾ കാണുക"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"ആപ്പുകൾ മാറുക"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"പൂർത്തിയായി"</string>
<string name="gesture_error_title" msgid="469064941635578511">"വീണ്ടും ശ്രമിക്കുക!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"മടങ്ങുക"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"കൊള്ളാം!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"അടുത്തിടെയുള്ള ആപ്പുകൾ കാണുക എന്ന ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"സമീപകാല ആപ്പുകൾ കാണുന്നതിന്, നിങ്ങളുടെ ടച്ച്പാഡിൽ മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്‌ത് പിടിക്കുക"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ആപ്പുകൾ മാറുക"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"കൊള്ളാം!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"ആപ്പുകൾ മാറൽ ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"എല്ലാ ആപ്പുകളും കാണുക"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"നിങ്ങളുടെ കീബോർഡിലെ ആക്ഷൻ കീ അമർത്തുക"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 31b058bce4f9..6effa0a5234b 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Шинэчилж байна"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Ажлын профайл"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Нислэгийн горим"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>-т та дараагийн сэрүүлгээ сонсохгүй"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> цагт"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>-т"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системийн аппууд"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Олон ажил зэрэг хийх"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Дэлгэцийг хуваах"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Оролт"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Аппын товчлол"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Одоогийн апп"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Товчлолыг өөрчлөх"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Товчлолыг хасах уу?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Өгөгдмөл рүү буцааж шинэчлэх үү?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Энэ товчлолыг үүсгэхийн тулд Тусгай товч болон өөр нэг эсвэл түүнээс олон товчийг хамтад нь дарна уу"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Энэ нь таны захиалгат товчлолыг бүрмөсөн устгана."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Энэ нь таны бүх захиалгат товчлолыг бүрмөсөн устгана."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Товчлолууд хайх"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Тэгье, шинэчилье"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Цуцлах"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Товч дарна уу"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Товчийн хослолыг аль хэдийн ашиглаж байна. Өөр хослол туршиж үзнэ үү."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Товчлол тохируулах боломжгүй."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Товчлол нэмэх"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Буцах"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Нүүр хуудас руу очих"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Саяхны аппуудыг харах"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Апп сэлгэх"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Болсон"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Дахин оролдоно уу!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Буцах"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Сайн байна!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Та саяхны аппуудыг харах зангааг гүйцэтгэсэн."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Саяхны аппуудыг харахын тулд мэдрэгч самбар дээрээ гурван хуруугаараа дээш шудраад, удаан дарна уу"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Апп сэлгэх"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Үнэхээр сайн ажиллалаа!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Та апп хооронд сэлгэх зангааг гүйцэтгэлээ."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Бүх аппыг харах"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Гар дээрх тусгай товчлуурыг дарна уу"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 4beed1731b97..77fd538ed398 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"अपडेट करत आहे"</string>
<string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाईल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"विमान मोड"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"तुम्ही तुमचा <xliff:g id="WHEN">%1$s</xliff:g> वाजता होणारा पुढील अलार्म ऐकणार नाही"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> वाजता"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> रोजी"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"सिस्टीम अ‍ॅप्स"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"मल्टिटास्किंग"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"स्प्लिट स्क्रीन"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"इनपुट"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"अ‍ॅप शॉर्टकट"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"सध्याचे अ‍ॅप"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"मागे जा"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"होमवर जा"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"अलीकडील अ‍ॅप्स पहा"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"अ‍ॅप्स स्विच करा"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"पूर्ण झाले"</string>
<string name="gesture_error_title" msgid="469064941635578511">"पुन्हा प्रयत्न करा!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"मागे जा"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"उत्तम कामगिरी!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"तुम्ही अलीकडील ॲप्स पाहण्याचे जेश्चर पूर्ण केले आहे."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"अलीकडील अ‍ॅप्स पाहण्यासाठी, तुमच्या टचपॅडवर तीन बोटांनी वर स्वाइप करून धरून ठेवा"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"अ‍ॅप्स स्विच करा"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"उत्तम कामगिरी!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"तुम्ही अ‍ॅप्स स्विच करणे जेश्चर पूर्ण केले आहे."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"सर्व अ‍ॅप्स पहा"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"तुमच्या कीबोर्डवर अ‍ॅक्शन की प्रेस करा"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index ea1f79bac39c..7aa95f09404d 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Mengemaskinikan"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mod pesawat"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Anda tidak akan mendengar penggera yang seterusnya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"pada <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"pada <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apl sistem"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Berbilang tugas"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Skrin pisah"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Pintasan apl"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Apl Semasa"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Kembali"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Akses laman utama"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Lihat apl terbaharu"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Tukar apl"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Selesai"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Cuba lagi!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kembali"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Syabas!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Anda telah melengkapkan gerak isyarat lihat apl terbaharu."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Untuk melihat apl terbaharu, leret ke atas dan tahan menggunakan tiga jari pada pad sentuh anda"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Tukar apl"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bagus!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Anda telah melengkapkan gerak isyarat menukar apl."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Lihat semua apl"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tekan kekunci tindakan pada papan kekunci anda"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 044c1aa43cb8..d973d4066774 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"အပ်ဒိတ်လုပ်နေသည်"</string>
<string name="status_bar_work" msgid="5238641949837091056">"အလုပ် ပရိုဖိုင်"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"လေယာဉ်ပျံမုဒ်"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> ၌သင့်နောက်ထပ် နှိုးစက်ကို ကြားမည်မဟုတ်ပါ"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ၌"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> တွင်"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"စနစ် အက်ပ်များ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"တစ်ပြိုင်နက် များစွာလုပ်ခြင်း"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"မျက်နှာပြင် ခွဲ၍ပြသခြင်း"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ထည့်သွင်းမှု"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"အက်ပ်ဖြတ်လမ်းလင့်ခ်များ"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"လက်ရှိအက်ပ်"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"ဖြတ်လမ်းများ စိတ်ကြိုက်ပြင်ဆင်ခြင်း"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ဖြတ်လမ်းလင့်ခ် ဖယ်ရှားမလား။"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"မူရင်းသို့ ပြန်လည်ပြင်ဆင်သတ်မှတ်မလား။"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"ဤဖြတ်လမ်းလင့်ခ် ပြုလုပ်ရန်အတွက် ‘လုပ်ဆောင်ချက်ကီး’ နှင့် တစ်ခု (သို့) တစ်ခုထက်ပိုသော အခြားကီးကို အတူနှိပ်ပါ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"၎င်းသည် သင့်စိတ်ကြိုက် ဖြတ်လမ်းလင့်ခ်ကို အပြီးဖျက်ပါမည်။"</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"၎င်းသည် သင့်စိတ်ကြိုက်ဖြတ်လမ်းလင့်ခ်အားလုံးကို အပြီးဖျက်မည်။"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ရှာဖွေစာလုံး ဖြတ်လမ်း"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ပြင်ဆင်ရန်"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"မလုပ်တော့"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ကီးကို နှိပ်ပါ"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"ကီးပေါင်းစပ်ခြင်းကို သုံးနေပြီးဖြစ်သည်။ အခြားပေါင်းစပ်မှုတစ်ခု စမ်းကြည့်ပါ။"</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"ဖြတ်လမ်းလင့်ခ် သတ်မှတ်၍မရပါ။"</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"ဖြတ်လမ်းလင့်ခ် ထည့်ရန်"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"နောက်သို့"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ပင်မစာမျက်နှာသို့ သွားရန်"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"မကြာသေးမီကအက်ပ်များကို ကြည့်ရန်"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"အက်ပ်များကူးပြောင်းခြင်း"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ပြီးပြီ"</string>
<string name="gesture_error_title" msgid="469064941635578511">"ထပ်စမ်းကြည့်ပါ။"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ပြန်သွားရန်"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"တော်ပါပေသည်။"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"မကြာသေးမီကအက်ပ်များကို ကြည့်ခြင်းလက်ဟန် သင်ခန်းစာပြီးပါပြီ။"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"လတ်တလောအက်ပ်များကြည့်ရန် တာ့ချ်ပက်တွင် လက်သုံးချောင်းဖြင့် အပေါ်သို့ပွတ်ဆွဲပြီး ဖိထားပါ"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"အက်ပ်များကူးပြောင်းခြင်း"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"တော်ပါပေသည်။"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"အက်ပ်ပြောင်းလက်ဟန် လုပ်ပြီးပါပြီ။"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"အက်ပ်အားလုံးကို ကြည့်ခြင်း"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ကီးဘုတ်တွင် လုပ်ဆောင်ချက်ကီး နှိပ်ပါ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 0c7ff3cd341b..0d9e2760614c 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Oppdaterer"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work-profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flymodus"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Du hører ikke neste innstilte alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"kl. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systemapper"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Delt skjerm"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inndata"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-snarveier"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktiv app"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Tilpass snarveier"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vil du fjerne hurtigtasten?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vil du tilbakestille til standard?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"For å opprette denne snarveien, trykk på handlingstasten og én eller flere andre taster samtidig"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Dette fører til at den egendefinerte hurtigtasten slettes permanent."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Dette fører til at alle de egendefinerte snarveiene dine slettes permanent."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Søk etter snarveier"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Tilbakestill"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Avbryt"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Trykk på tasten"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Tastekombinasjonen brukes allerede. Prøv en annen kombinasjon."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Kan ikke angi snarveien."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Legg til snarvei"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Gå tilbake"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Gå til startsiden"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Se nylige apper"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Bytt app"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Ferdig"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Prøv på nytt."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gå tilbake"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bra jobbet!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du har fullført bevegelsen for å se nylige apper."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"For å se nylige apper, sveip opp og hold med tre fingre på styreflaten"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Bytt app"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bra jobbet!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Du har fullført bytt-app-bevegelsen."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Se alle apper"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Trykk på handlingstasten på tastaturet"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index e2fe85aff01f..b1911596b2a1 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"अपडेट गरिँदै छ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाइल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"हवाइजहाज मोड"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"तपाईँले आफ्नो अर्को अलार्म <xliff:g id="WHEN">%1$s</xliff:g> सुन्नुहुने छैन"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> मा"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> मा"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"सिस्टम एपहरू"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"मल्टिटास्किङ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"स्प्लिट स्क्रिन"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"इनपुट"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"एपका सर्टकटहरू"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"हालको एप"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"सर्टकटहरू कस्टमाइज गर्नुहोस्"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"सर्टकट हटाउने हो?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"सर्टकट रिसेट गरी डिफल्ट बनाउने हो?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"यो सर्टकट बनाउन एक्सन की र एउटा वा सोभन्दा बढी की एकैचोटि थिच्नुहोस्"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"यसो गर्नुभयो भने तपाईंको कस्टम सर्टकट सदाका लागि मेटिने छ।"</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"तपाईंले यसो गर्नुभयो भने तपाईंका सबै कस्टम सर्टकटहरू सदाका लागि मेटाइने छन्।"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"सर्टकटहरू खोज्नुहोस्"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"अँ, रिसेट गर्नुहोस्"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"रद्द गर्नुहोस्"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"की थिच्नुहोस्"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"यो की कम्बिनेसन प्रयोग गरिसकिएको छ। अर्कै कम्बिनेसन असाइन गरी हेर्नुहोस्।"</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"सर्टकट सेट गर्न सकिएन।"</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"सर्टकट हाल्नुहोस्"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"पछाडि जानुहोस्"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"होम स्क्रिनमा जानुहोस्"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"हालसालै चलाइएका एपहरू हेर्नुहोस्"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"एपहरू बदल्नुहोस्"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"सम्पन्न भयो"</string>
<string name="gesture_error_title" msgid="469064941635578511">"फेरि प्रयास गर्नुहोस्!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"पछाडि जानुहोस्"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"अद्भुत!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"तपाईंले जेस्चर प्रयोग गरी हालसालै चलाइएका एपहरू हेर्ने तरिका सिक्नुभएको छ।"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"हालसालैका एपहरू हेर्न तीन औँला प्रयोग गरी टचप्याडमा माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"एपहरू बदल्नुहोस्"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"अद्भुत!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"तपाईंले एपहरू बदल्ने जेस्चर प्रयोग गर्ने तरिका सिक्नुभयो।"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"सबै एपहरू हेर्नुहोस्"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"आफ्नो किबोर्डमा भएको एक्सन की थिच्नुहोस्"</string>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index a77f5e4629c1..326be99b8a65 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -108,8 +108,14 @@
<color name="people_tile_background">@color/material_dynamic_secondary20</color>
- <!-- Dark Theme colors for notification shade/scrim -->
- <color name="shade_panel">@android:color/system_accent1_900</color>
+ <!-- Dark theme base colors for notification shade/scrim, the alpha component is adjusted
+ programmatically to match the spec -->
+ <color name="shade_panel">@android:color/system_accent1_800</color>
+ <color name="surface_effect_0">@android:color/system_accent1_800</color>
+
+ <!-- todo(b/388891904) Remove updated color references once they are available. -->
+ <color name="shade_panel_base">@color/shade_panel</color>
+ <color name="notification_scrim_base">@color/surface_effect_0</color>
<!-- Keyboard shortcut helper dialog -->
<color name="ksh_key_item_color">@*android:color/system_on_surface_variant_dark</color>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index bae1489af501..a3d8856625f4 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updaten"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuig­modus"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Je hoort je volgende wekker niet <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"om <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"op <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systeem-apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Gesplitst scherm"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Invoer"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-sneltoetsen"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Huidige app"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Snelkoppelingen aanpassen"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Sneltoets verwijderen?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Resetten naar standaard?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Als je deze sneltoets wilt maken, druk je tegelijkertijd op de actietoets en een of meer andere toetsen"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Hiermee wordt je aangepaste sneltoets definitief verwijderd."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Hiermee worden al je aangepaste snelkoppelingen definitief verwijderd."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sneltoetsen zoeken"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, resetten"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Annuleren"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Druk op een toets"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Toetsencombinatie is al in gebruik. Probeer een andere combinatie."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Sneltoets kan niet worden ingesteld."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Snelkoppeling toevoegen"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Terug"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Naar startscherm"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Recente apps bekijken"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Wisselen tussen apps"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klaar"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Probeer het nog eens."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Terug"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Goed gedaan!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Je weet nu hoe je het gebaar Recente apps bekijken maakt."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Als je recente apps wilt bekijken, swipe je met 3 vingers omhoog op de touchpad en houd je vast"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Wisselen tussen apps"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Goed werk!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Je weet nu hoe je het gebaar om van app te wisselen maakt."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Alle apps bekijken"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Druk op de actietoets op het toetsenbord"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 1201e4ccbe93..fa89a4858cdc 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"ଅପଡେଟ ହେଉଛି"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ୱର୍କ ପ୍ରୋଫାଇଲ୍‌"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ଏୟାରପ୍ଲେନ ମୋଡ"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>ବେଳେ ଆପଣ ନିଜର ପରବର୍ତ୍ତୀ ଆଲାର୍ମ ଶୁଣିପାରିବେ ନାହିଁ"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ହେଲେ"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> ବେଳେ"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ସିଷ୍ଟମ ଆପ୍ସ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ମଲ୍ଟିଟାସ୍କିଂ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ଇନପୁଟ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ଆପ ସର୍ଟକଟ"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ବର୍ତ୍ତମାନର ଆପ"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"ସର୍ଟକଟଗୁଡ଼ିକୁ କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ସର୍ଟକଟକୁ କାଢ଼ି ଦେବେ?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ଡିଫଲ୍ଟରେ ପୁଣି ରିସେଟ କରିବେ?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"ଏହି ସର୍ଟକଟ ତିଆରି କରିବାକୁ ଏକାଠି ଆକ୍ସନ କୀ କିମ୍ବା ଗୋଟିଏ ବା ଅଧିକ ଅନ୍ୟ କୀ ଦବାନ୍ତୁ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ଏହା ଆପଣଙ୍କ କଷ୍ଟମ ସର୍ଟକଟକୁ ସ୍ଥାୟୀ ଭାବେ ଡିଲିଟ କରିଦେବ।"</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ଏହା ଆପଣଙ୍କର ସମସ୍ତ କଷ୍ଟମ ସର୍ଟକଟକୁ ସ୍ଥାୟୀ ଭାବେ ଡିଲିଟ କରିଦେବ।"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ସର୍ଚ୍ଚ ସର୍ଟକଟ"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ହଁ, ରିସେଟ କରନ୍ତୁ"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ବାତିଲ କରନ୍ତୁ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"କୀ ଦବାନ୍ତୁ"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"କୀ କମ୍ବିନେସନ ପୂର୍ବରୁ ବ୍ୟବହାର କରାଯାଉଛି। ଅନ୍ୟ ଏକ କମ୍ବିନେସନ ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ।"</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"ସର୍ଟକଟ ସେଟ କରାଯାଇପାରିବ ନାହିଁ।"</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"ସର୍ଟକଟ ଯୋଗ କରନ୍ତୁ"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ପଛକୁ ଫେରନ୍ତୁ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ହୋମକୁ ଯାଆନ୍ତୁ"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ବର୍ତ୍ତମାନର ଆପ୍ସ ଭ୍ୟୁ କରନ୍ତୁ"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"ଆପ୍ସକୁ ସୁଇଚ କରନ୍ତୁ"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ହୋଇଗଲା"</string>
<string name="gesture_error_title" msgid="469064941635578511">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ପଛକୁ ଫେରନ୍ତୁ"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ବଢ଼ିଆ କାମ!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ଆପଣ ବର୍ତ୍ତମାନର ଆପ୍ସ ଜେଶ୍ଚରକୁ ଭ୍ୟୁ କରିବା ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"ବର୍ତ୍ତମାନର ଆପ୍ସ ଭ୍ୟୁ କରିବାକୁ, ଆପଣଙ୍କ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠି ବ୍ୟବହାର କରି ଉପରକୁ ସ୍ୱାଇପ କରି ଧରି ରଖନ୍ତୁ"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ଆପ୍ସକୁ ସୁଇଚ କରନ୍ତୁ"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"ବଢ଼ିଆ କାମ!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"ଆପଣ ସୁଇଚ ଆପ୍ସ ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"ସବୁ ଆପ ଭ୍ୟୁ କରନ୍ତୁ"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ଆପଣଙ୍କର କୀବୋର୍ଡରେ ଆକ୍ସନ କୀ\'କୁ ଦବାନ୍ତୁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 807abf3df40e..a0c06122b7a4 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"ਅੱਪਡੇਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"ਤੁਸੀਂ <xliff:g id="WHEN">%1$s</xliff:g> ਵਜੇ ਆਪਣਾ ਅਗਲਾ ਅਲਾਰਮ ਨਹੀਂ ਸੁਣੋਗੇ"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ਵਜੇ"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> ਵਜੇ"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"ਸਿਸਟਮ ਐਪਾਂ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ਮਲਟੀਟਾਸਕਿੰਗ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ਇਨਪੁੱਟ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ਐਪ ਸ਼ਾਰਟਕੱਟ"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ਮੌਜੂਦਾ ਐਪ"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"ਸ਼ਾਰਟਕੱਟਾਂ ਨੂੰ ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ਕੀ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਹਟਾਉਣਾ ਹੈ?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ਕੀ ਵਾਪਸ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ \'ਤੇ ਰੀਸੈੱਟ ਕਰਨਾ ਹੈ?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"ਇਸ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਬਣਾਉਣ ਲਈ, ਕਾਰਵਾਈ ਕੁੰਜੀ ਅਤੇ ਇੱਕ ਜਾਂ ਇੱਕ ਤੋਂ ਵੱਧ ਕੁੰਜੀਆਂ ਨੂੰ ਇਕੱਠੇ ਦਬਾਓ"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ਇਸ ਨਾਲ ਤੁਹਾਡੇ ਵਿਉਂਤੇ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਪੱਕੇ ਤੌਰ \'ਤੇ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ਇਸ ਨਾਲ ਤੁਹਾਡੇ ਸਾਰੇ ਵਿਉਂਤਬੱਧ ਸ਼ਾਰਟਕੱਟ ਪੱਕੇ ਤੌਰ \'ਤੇ ਮਿਟ ਜਾਣਗੇ।"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ਸ਼ਾਰਟਕੱਟ ਖੋਜੋ"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ਹਾਂ, ਰੀਸੈੱਟ ਕਰੋ"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ਰੱਦ ਕਰੋ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ਕੁੰਜੀ ਦਬਾਓ"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"ਕੁੰਜੀ ਸੁਮੇਲ ਪਹਿਲਾਂ ਹੀ ਵਰਤੋਂ ਵਿੱਚ ਹੈ। ਕੋਈ ਹੋਰ ਸੁਮੇਲ ਵਰਤ ਕੇ ਦੇਖੋ।"</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਸੈੱਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"ਸ਼ਾਰਟਕੱਟ ਸ਼ਾਮਲ ਕਰੋ"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ਵਾਪਸ ਜਾਓ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ਹੋਮ \'ਤੇ ਜਾਓ"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ਹਾਲੀਆ ਐਪਾਂ ਦੇਖੋ"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"ਐਪਾਂ ਵਿਚਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ਹੋ ਗਿਆ"</string>
<string name="gesture_error_title" msgid="469064941635578511">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ਵਾਪਸ ਜਾਓ"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ਬਹੁਤ ਵਧੀਆ!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ਤੁਸੀਂ \'ਹਾਲੀਆ ਐਪਾਂ ਦੇਖੋ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ ਹੈ।"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"ਹਾਲੀਆ ਐਪਾਂ ਦੇਖਣ ਲਈ, ਆਪਣੇ ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਵਰਤ ਕੇ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰਕੇ ਰੋਕ ਕੇ ਰੱਖੋ"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ਐਪਾਂ ਵਿਚਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"ਬਹੁਤ ਵਧੀਆ!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"ਤੁਸੀਂ ਐਪ ਸਵਿੱਚ ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ।"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"ਸਾਰੀਆਂ ਐਪਾਂ ਦੇਖੋ"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ਆਪਣੇ ਕੀ-ਬੋਰਡ \'ਤੇ ਕਾਰਵਾਈ ਕੁੰਜੀ ਨੂੰ ਦਬਾਓ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 2554396b596f..031311741a55 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Aktualizuję"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil służbowy"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Tryb samolotowy"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nie usłyszysz swojego następnego alarmu <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"o <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"w: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikacje systemowe"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Wielozadaniowość"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podzielony ekran"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Wprowadzanie"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Skróty do aplikacji"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Bieżąca aplikacja"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Wróć"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Otwórz stronę główną"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Wyświetlanie ostatnich aplikacji"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Przełączanie aplikacji"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotowe"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Spróbuj jeszcze raz"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Wróć"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Brawo!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Znasz już gest wyświetlania ostatnio używanych aplikacji."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Aby wyświetlić ostatnie aplikacje, przesuń 3 palcami w górę na touchpadzie i przytrzymaj"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Przełączanie aplikacji"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Brawo!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Gest do przełączania aplikacji został opanowany."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Wyświetl wszystkie aplikacje"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Naciśnij klawisz działania na klawiaturze"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 9d73d06c4806..e21554cb9e6f 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Atualizando"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Você não ouvirá o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps do sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefas"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Tela dividida"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Voltar"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir para a página inicial"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver os apps recentes"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Mudar de app"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluído"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Tente de novo"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Muito bem!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Você concluiu o gesto para ver os apps recentes."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Se quiser ver os apps recentes, deslize para cima e pressione o touchpad com três dedos"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Mudar de app"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Muito bem!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Você concluiu o gesto para mudar de app."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todos os apps"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pressione a tecla de ação no teclado"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index d7fcb758c71f..f5c29d485130 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"A atualizar"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avião"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Não vai ouvir o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"em <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps do sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ecrã dividido"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Voltar"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Aceder ao ecrã principal"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver apps recentes"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Mudar de app"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluir"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Tente novamente!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Muito bem!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Concluiu o gesto para ver as apps recentes."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Para ver as apps recentes, deslize rapidamente para cima sem soltar com 3 dedos no touchpad"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Mudar de app"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Muito bem!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Concluiu o gesto para alternar entre apps."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas as apps"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Prima a tecla de ação no teclado"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 9d73d06c4806..e21554cb9e6f 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Atualizando"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Você não ouvirá o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Apps do sistema"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefas"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Tela dividida"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Voltar"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir para a página inicial"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver os apps recentes"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Mudar de app"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluído"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Tente de novo"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Muito bem!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Você concluiu o gesto para ver os apps recentes."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Se quiser ver os apps recentes, deslize para cima e pressione o touchpad com três dedos"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Mudar de app"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Muito bem!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Você concluiu o gesto para mudar de app."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todos os apps"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pressione a tecla de ação no teclado"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 3991604d2cee..7a9ea1fa10fd 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Se actualizează"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil de serviciu"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mod Avion"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nu vei auzi următoarea alarmă <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"la <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplicații de sistem"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ecran împărțit"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Intrare"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Comenzi rapide pentru aplicații"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicația actuală"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Personalizează comenzile rapide"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Elimini comanda rapidă?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Resetezi la valorile prestabilite?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Pentru a crea această comandă rapidă, apasă tasta de acțiuni și una sau mai multe taste simultan"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Astfel, se va șterge definitiv comanda rapidă personalizată."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Astfel, se vor șterge definitiv toate comenzile rapide personalizate."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Comenzi directe de căutare"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Resetează"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anulează"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Apasă tasta"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Combinația de taste este deja folosită. Încearcă altă combinație."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Comanda rapidă nu poate fi setată."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Adaugă o comandă rapidă"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Înapoi"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Înapoi la pagina de pornire"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Vezi aplicațiile recente"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Comută între aplicații"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gata"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Încearcă din nou!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Înapoi"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Excelent!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ai finalizat gestul pentru afișarea aplicațiilor recente."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Ca să vezi aplicațiile recente, glisează în sus și ține apăsat cu trei degete pe touchpad"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Comută între aplicații"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Excelent!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Ai finalizat gestul de trecere la altă aplicație."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Vezi toate aplicațiile"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Apasă tasta de acțiuni de pe tastatură"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index ff5bd2e50537..f5b33742c8cb 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Обновление"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Рабочий профиль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим полета"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Следующий будильник: <xliff:g id="WHEN">%1$s</xliff:g>. Звук отключен."</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системные приложения"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Многозадачность"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Разделение экрана"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ввод"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Приложения"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Это приложение"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Настройки сочетаний клавиш"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Удалить сочетание клавиш?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Сбросить настройки?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Чтобы сохранить сочетание клавиш, нажмите одновременно клавишу действия и одну или несколько дополнительных клавиш"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Настроенное сочетание будет безвозвратно удалено."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Все настроенные сочетания клавиш будут безвозвратно удалены."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Найти"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Сбросить"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Отмена"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Нажмите клавишу"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Это сочетание клавиш уже используется. Попробуйте другое."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Это сочетание клавиш выбрать нельзя."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Добавить сочетание клавиш"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"На главный экран"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Просмотр недавних приложений"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Переход в другое приложение"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Попробуйте ещё раз"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Отлично!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Вы выполнили жест для просмотра недавних приложений."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Чтобы увидеть недавние приложения, проведите по сенсорной панели тремя пальцами вверх и удерживайте."</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Переход в другое приложение"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Отлично!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Вы выполнили жест перехода в другое приложение."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Все приложения"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Нажмите клавишу действия на клавиатуре."</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 46ee1df1ee45..a9ea2ae68a89 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"යාවත්කාලීන කිරීම"</string>
<string name="status_bar_work" msgid="5238641949837091056">"කාර්යාල පැතිකඩ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ගුවන්යානා ප්‍රකාරය"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"ඔබට ඔබේ ඊළඟ එලාමය <xliff:g id="WHEN">%1$s</xliff:g> නොඇසෙනු ඇත"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ට"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> දී"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"පද්ධති යෙදුම්"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"බහුකාර්ය"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"බෙදුම් තිරය"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ආදානය"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"යෙදුම් කෙටිමං"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"වත්මන් යෙදුම"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ආපසු යන්න"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"මුල් පිටුවට යන්න"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"මෑත යෙදුම් බලන්න"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"යෙදුම් මාරු කරන්න"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"නිමයි"</string>
<string name="gesture_error_title" msgid="469064941635578511">"නැවත උත්සාහ කරන්න!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ආපස්සට යන්න"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"අනර්ඝ වැඩක්!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ඔබ මෑත යෙදුම් ඉංගිත බැලීම සම්පූර්ණ කර ඇත."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"මෑත කාලීන යෙදුම් බැලීමට, ඔබේ ස්පර්ශක පෑඩයේ ඇඟිලි තුනක් භාවිතයෙන් ඉහළට ස්වයිප් කරගෙන සිටින්න"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"යෙදුම් මාරු කරන්න"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"අනර්ඝ වැඩක්!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"ඔබ යෙදුම් මාරු කිරීමේ ඉංගිතය සම්පූර්ණ කර ඇත."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"සියලු යෙදුම් බලන්න"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ඔබේ යතුරු පුවරුවේ ක්‍රියාකාරී යතුර ඔබන්න"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 0aa3be7dd5d7..406f423a18d0 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Aktualizuje sa"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Pracovný profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim v lietadle"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Váš budík o <xliff:g id="WHEN">%1$s</xliff:g> sa nespustí"</string>
<string name="alarm_template" msgid="2234991538018805736">"o <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systémové aplikácie"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Rozdelená obrazovka"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vstup"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Skratky aplikácií"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuálna aplikácia"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Prechod späť"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Prejsť na plochu"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Zobrazenie nedávnych aplikácií"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Prepínanie aplikácií"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hotovo"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Skúste to znova."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Prejdenie späť"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Skvelé!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Použili ste gesto na zobrazenie nedávnych aplikácií."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Ak si chcete zobraziť nedávne aplikácie, potiahnite troma prstami na touchpade nahor a pridržte ich"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Prepínanie aplikácií"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Skvelé!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Dokončili ste gesto na prepnutie aplikácií."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Zobrazenie všetkých aplikácií"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Stlačte na klávesnici akčný kláves"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index e929f9290726..7127bed39f6b 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Posodabljanje"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil za Android Work"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način za letalo"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Naslednjega alarma ob <xliff:g id="WHEN">%1$s</xliff:g> ne boste slišali"</string>
<string name="alarm_template" msgid="2234991538018805736">"ob <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"ob tem času: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistemske aplikacije"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Večopravilnost"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Razdeljen zaslon"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vnos"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Bližnjice do aplikacij"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutna aplikacija"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Prilagajanje bližnjic"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Želite odstraniti bližnjico?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Želite ponastaviti na privzete bližnjice?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Če želite ustvariti to bližnjico, hkrati pritisnite tipko za dejanja in eno ali več drugih tipk"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"S tem boste trajno izbrisali bližnjico po meri."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"S tem boste trajno izbrisali vse bližnjice po meri."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Iskanje po bližnjicah"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Prekliči"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite tipko"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Kombinacija tipk je že v uporabi. Poskusite drugo kombinacijo."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Bližnjice ni mogoče nastaviti."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Dodajanje bližnjice"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Pomik nazaj"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Pomik na začetni zaslon"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ogled nedavnih aplikacij"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Preklop aplikacij"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Končano"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Poskusite znova"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazaj"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Odlično!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Izvedli ste potezo za ogled nedavnih aplikacij."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Za ogled nedavnih aplikacij povlecite s tremi prsti navzgor po sledilni ploščici in pridržite"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Preklop aplikacij"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Odlično!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Izvedli ste potezo za preklop med aplikacijami."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Ogled vseh aplikacij"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipko za dejanja na tipkovnici"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index fe9abbb585b9..b02434a810a9 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Po përditësohet"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profili i punës"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modaliteti i aeroplanit"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nuk do ta dëgjosh alarmin e radhës në <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"në <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"në <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Aplikacionet e sistemit"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Kryerja e shumë detyrave"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekrani i ndarë"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Hyrja"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Shkurtoret e aplikacionit"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplikacioni aktual"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Personalizo shkurtoret"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Të hiqet shkurtorja?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Të rivendosen përsëri te parazgjedhjet?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Për të krijuar këtë shkurtore, shtyp tastin e veprimit dhe një ose disa nga tastet e tjera së bashku"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Kjo do ta fshijë përgjithmonë shkurtoren tënde të personalizuar."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Kjo do të fshijë përgjithmonë të gjitha shkurtoret e tua të personalizuara."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Kërko për shkurtoret"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Po"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anulo"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Shtyp tastin"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Kombinimi i tasteve është tashmë në përdorim. Provo një kombinim tjetër."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Shkurtorja nuk mund të caktohet."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Shto shkurtore"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Kthehu prapa"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Shko tek ekrani bazë"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Shiko aplikacionet e fundit"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Ndërro aplikacionet"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"U krye"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Provo përsëri!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kthehu prapa"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Punë e shkëlqyer!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Përfundove gjestin për shikimin e aplikacioneve të fundit."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Për të shikuar aplikacionet e fundit, rrëshqit shpejt lart dhe mbaj shtypur me tre gishta në bllokun me prekje"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Ndërro aplikacionet"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Punë e shkëlqyer!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"E ke përfunduar gjestin e ndërrimit të aplikacioneve."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Shiko të gjitha aplikacionet"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Shtyp tastin e veprimit në tastierë"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 56cac9863626..70967419276b 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ажурира се"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Пословни профил"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим рада у авиону"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Нећете чути следећи аларм у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системске апликације"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Обављање више задатака истовремено"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Подељени екран"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Унос"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Пречице за апликације"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Актуелна апликација"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Иди на почетни екран"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прикажи недавно коришћене апликације"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Пређи на другу апликацију"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Пробајте поново."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Одлично!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Довршили сте покрет за приказивање недавно коришћених апликација."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Да бисте прегледали недавне апликације, превуците нагоре и задржите са три прста на тачпеду"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Пређи на другу апликацију"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Одлично!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Довршили сте покрет за промену апликација."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Прикажи све апликације"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Притисните тастер радњи на тастатури"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 2d07a4307bf3..4e64e5a80d31 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Uppdaterar"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Jobbprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flygplansläge"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nästa alarm, kl. <xliff:g id="WHEN">%1$s</xliff:g>, kommer inte att höras"</string>
<string name="alarm_template" msgid="2234991538018805736">"kl. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"kl. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Systemappar"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multikörning"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Delad skärm"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inmatning"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Genvägar till appar"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuell app"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Anpassa kortkommandon"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Vill du ta bort kortkommandot?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vill du återställa till standardinställningarna?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Skapa det här kortkommandot genom att trycka på åtgärdstangenten och en eller flera andra tangenter samtidigt"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Det anpassade kortkommandot raderas permanent."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Alla anpassade genvägar raderas permanent."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sökgenvägar"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, återställ"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Avbryt"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tryck på tangenten"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Tangentkombinationen används redan. Testa en annan kombination."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Det går inte att ställa in kortkommandot."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Lägg till genväg"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Tillbaka"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Återvänd till startsidan"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Se de senaste apparna"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Byta app"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klar"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Försök igen!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Tillbaka"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bra jobbat!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du är klar med rörelsen för att se de senaste apparna."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Svep uppåt på styrplattan med tre fingrar och håll kvar för att se nyligen använda appar"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Byta app"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Bra jobbat!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Du har slutfört rörelsen för att byta app"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Visa alla appar"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tryck på åtgärdstangenten på tangentbordet"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 1ee8f61e27a2..ecba5597e588 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Inasasisha"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Wasifu wa kazini"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Hali ya ndegeni"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Hutasikia kengele yako inayofuata ya saa <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"saa <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"siku ya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Programu za mfumo"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Majukumu mengi"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Gawa skrini"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Kifaa cha kuingiza data"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Njia za mikato za programu"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Programu Inayotumika Sasa"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Kuweka mapendeleo ya njia za mkato"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Ungependa kuondoa njia ya mkato?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Ungependa kurejesha njia za mkato chaguomsingi?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Ili kuanzisha njia hii ya mkato, bonyeza kitufe cha Vitendo na ufunguo mmoja au zaidi kwa pamoja"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Hatua hii itaondoa kabisa njia yako maalum ya mkato."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Hatua hii itafuta kabisa njia zako zote maalum za mkato."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Njia mkato za kutafutia"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ndiyo"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Acha"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Bonyeza kitufe"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Tayari unatumia mchanganyiko huu wa vitufe. Jaribu mchanganyiko mwingine."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Haiwezi kuweka njia ya mkato."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Weka njia ya mkato"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Rudi nyuma"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Nenda kwenye ukurasa wa mwanzo"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Angalia programu za hivi majuzi"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Badilisha programu"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Nimemaliza"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Jaribu tena!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Rudi nyuma"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Kazi nzuri!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Umekamilisha mafunzo ya mguso wa kuangalia programu za hivi majuzi."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Telezesha vidole vitatu juu na ushikilie kwenye padi yako ya kugusa ili uangalie programu za hivi majuzi"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Badilisha programu"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Kazi nzuri!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Umekamilisha mafunzo kuhusu mguso wa kubadili programu."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Angalia programu zote"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Bonyeza kitufe cha vitendo kwenye kibodi yako"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 7ef59b11633c..d6e5be4c1bd5 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"புதுப்பிக்கிறது"</string>
<string name="status_bar_work" msgid="5238641949837091056">"பணிக் கணக்கு"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"விமானப் பயன்முறை"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"அடுத்த அலாரத்தை <xliff:g id="WHEN">%1$s</xliff:g> மணிக்கு கேட்க மாட்டீர்கள்"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> மணிக்கு"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> மணிக்கு"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"சிஸ்டம் ஆப்ஸ்"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"பல வேலைகளைச் செய்தல்"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"திரைப் பிரிப்பு"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"உள்ளீடு"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ஆப்ஸ் ஷார்ட்கட்கள்"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"தற்போதைய ஆப்ஸ்"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"ஷார்ட்கட்களைப் பிரத்தியேகமாக்குதல்"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ஷார்ட்கட்டை அகற்றவா?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"மீண்டும் இயல்புநிலைக்கு மீட்டமைக்கவா?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"இந்த ஷார்ட்கட்டை உருவாக்க, ஆக்‌ஷன் பட்டனுடன் ஒன்று அல்லது அதற்கும் மேற்பட்ட பிற பட்டன்களை ஒன்றாக அழுத்துங்கள்"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"இது உங்கள் பிரத்தியேக ஷார்ட்கட்டை நிரந்தரமாக நீக்கும்."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"இது உங்கள் பிரத்தியேக ஷார்ட்கட்கள் அனைத்தையும் நிரந்தரமாக நீக்கும்."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ஷார்ட்கட்களைத் தேடுக"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ஆம். மீட்டமை"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ரத்துசெய்"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"பட்டனை அழுத்துங்கள்"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"பட்டன் சேர்க்கை ஏற்கெனவே பயன்பாட்டில் உள்ளது. வேறு பட்டன் சேர்க்கையை முயலவும்."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"ஷார்ட்கட்டை அமைக்க முடியாது."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"ஷார்ட்கட்டைச் சேர்க்கும்"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"பின்செல்"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"முகப்பிற்குச் செல்"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"சமீபத்திய ஆப்ஸைக் காட்டுதல்"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"ஆப்ஸுக்கிடையில் மாறுங்கள்"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"முடிந்தது"</string>
<string name="gesture_error_title" msgid="469064941635578511">"மீண்டும் முயலவும்!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"பின்செல்"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"அருமை!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"சமீபத்தில் பயன்படுத்திய ஆப்ஸுக்கான சைகை பயிற்சியை நிறைவுசெய்துவிட்டீர்கள்."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"சமீபத்திய ஆப்ஸைப் பார்க்க, உங்கள் டச்பேடில் மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்து பிடிக்கவும்"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ஆப்ஸுக்கிடையில் மாறுங்கள்"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"அருமை!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"ஆப்ஸுக்கிடையில் மாறும் சைகைப் பயிற்சியை முடித்துவிட்டீர்கள்."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"அனைத்து ஆப்ஸையும் காட்டு"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"உங்கள் கீபோர்டில் ஆக்‌ஷன் பட்டனை அழுத்தவும்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 190bc0ee109a..404e5a1308f4 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"అప్‌డేట్ చేస్తోంది"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ఆఫీస్ ప్రొఫైల్‌"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"విమానం మోడ్"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"మీరు <xliff:g id="WHEN">%1$s</xliff:g> సెట్ చేసిన మీ తర్వాత అలారం మీకు వినిపించదు"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>కి"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>కి"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"సిస్టమ్ యాప్‌లు"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"మల్టీ-టాస్కింగ్"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"స్ప్లిట్ స్క్రీన్"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ఇన్‌పుట్"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"యాప్ షార్ట్‌కట్‌లు"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ప్రస్తుత యాప్"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"షార్ట్‌కట్‌లను అనుకూలంగా మార్చండి"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"షార్ట్‌కట్‌ను తీసివేయాలా?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"తిరిగి ఆటోమేటిక్ సెట్టింగ్‌కు రీసెట్ చేయాలా?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"ఈ షార్ట్‌కట్‌ను క్రియేట్ చేయడానికి, యాక్షన్ కీని, ఒకటి లేదా అంత కంటే ఎక్కువ ఇతర కీలను కలిపి నొక్కండి"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ఇది మీ అనుకూల షార్ట్‌కట్‌ను శాశ్వతంగా తొలగిస్తుంది."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ఇది మీ అనుకూల షార్ట్‌కట్‌లన్నింటిని శాశ్వతంగా తొలగిస్తుంది."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"షార్ట్‌కట్‌లను వెతకండి"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"అవును, రీసెట్ చేయాలి"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"రద్దు చేయండి"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"కీని నొక్కండి"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"కీ కాంబినేషన్ ఇప్పటికే వినియోగంలో ఉంది. మరొక కాంబినేషన్‌ను ట్రై చేయండి."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"షార్ట్‌కట్‌ను సెట్ చేయడం సాధ్యం కాదు."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"షార్ట్‌కట్‌ను జోడించండి"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"వెనుకకు వెళ్లండి"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"మొదటి ట్యాబ్‌కు వెళ్లండి"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ఇటీవలి యాప్‌లను చూడండి"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"యాప్‌ల మధ్య మారండి"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"పూర్తయింది"</string>
<string name="gesture_error_title" msgid="469064941635578511">"మళ్లీ ట్రై చేయండి!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"వెనుకకు"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"చక్కగా పూర్తి చేశారు!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ఇటీవలి యాప్‌లను చూడడానికి ఉపయోగించే సంజ్ఞకు సంబంధించిన ట్యుటోరియల్‌ను మీరు పూర్తి చేశారు."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"ఇటీవలి యాప్‌లను చూడటానికి, మీ టచ్‌ప్యాడ్‌లో మూడు వేళ్లను ఉపయోగించి పైకి స్వైప్ చేసి, హోల్డ్ చేయండి"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"యాప్‌ల మధ్య మారండి"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"చక్కగా పూర్తి చేశారు!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"మీరు యాప్‌ల మధ్య మారేందుకు సంజ్ఞను పూర్తి చేశారు."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"అన్ని యాప్‌లను చూడండి"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"మీ కీబోర్డ్‌లో యాక్షన్ కీని నొక్కండి"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 3ee58e818c94..c67852bd826a 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"กำลังอัปเดต"</string>
<string name="status_bar_work" msgid="5238641949837091056">"โปรไฟล์งาน"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"โหมดบนเครื่องบิน"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"คุณจะไม่ได้ยินเสียงปลุกครั้งถัดไปในเวลา <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"เวลา <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"ในวันที่ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"แอประบบ"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"การทํางานหลายอย่างพร้อมกัน"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"แยกหน้าจอ"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"อินพุต"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"แป้นพิมพ์ลัดของแอป"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"แอปปัจจุบัน"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"ปรับแต่งแป้นพิมพ์ลัด"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"นำแป้นพิมพ์ลัดออกใช่ไหม"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"รีเซ็ตกลับเป็นค่าเริ่มต้นไหม"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"หากต้องการสร้างแป้นพิมพ์ลัดนี้ ให้กดปุ่มดำเนินการและปุ่มอื่นๆ อย่างน้อย 1 ปุ่มพร้อมกัน"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"การดำเนินการนี้จะลบแป้นพิมพ์ลัดที่กำหนดเองอย่างถาวร"</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"การดำเนินการนี้จะลบแป้นพิมพ์ลัดที่กำหนดเองทั้งหมดอย่างถาวร"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ค้นหาแป้นพิมพ์ลัด"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ใช่ รีเซ็ต"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"ยกเลิก"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"กดแป้น"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"มีการใช้แป้นที่กดร่วมกันนี้แล้ว โปรดลองใช้ชุดค่าผสมอื่น"</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"ตั้งค่าแป้นพิมพ์ลัดไม่ได้"</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"เพิ่มทางลัด"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ย้อนกลับ"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ไปที่หน้าแรก"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ดูแอปล่าสุด"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"เปลี่ยนแอป"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"เสร็จสิ้น"</string>
<string name="gesture_error_title" msgid="469064941635578511">"ลองอีกครั้งนะ"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ย้อนกลับ"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"เยี่ยมมาก"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"คุณทำท่าทางสัมผัสเพื่อดูแอปล่าสุดสำเร็จแล้ว"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"หากต้องการดูแอปล่าสุด ให้ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้บนทัชแพด"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"เปลี่ยนแอป"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"เก่งมาก"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"คุณทำท่าทางสัมผัสเพื่อสลับแอปเสร็จแล้ว"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"ดูแอปทั้งหมด"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"กดปุ่มดำเนินการบนแป้นพิมพ์"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 771a9e28c21e..cf2b2f043963 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ina-update"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profile sa trabaho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Airplane mode"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Hindi mo maririnig ang iyong susunod na alarm ng <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"ng <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"sa <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Mga system app"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Pag-multitask"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Mga shortcut ng app"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Kasalukuyang App"</string>
@@ -1474,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Bumalik"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Pumunta sa home"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Tingnan ang mga kamakailang app"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Lumipat ng app"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Tapos na"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Subukan ulit!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Bumalik"</string>
@@ -1493,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Magaling!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Nakumpleto mo ang galaw sa pag-view ng mga kamakailang app."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Para tingnan ang mga kamakailang app, mag-swipe pataas at i-hold gamit ang tatlong daliri sa iyong touchpad"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Lumipat ng app"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Magaling!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Nakumpleto mo na ang galaw para magpalipat-lipat sa mga app."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Tingnan ang lahat ng app"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pindutin ang action key sa iyong keyboard"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index f18c5535611f..8cd9eb36d180 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Güncelleniyor"</string>
<string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Uçak modu"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> olarak ayarlanmış bir sonraki alarmınızı duymayacaksınız"</string>
<string name="alarm_template" msgid="2234991538018805736">"saat: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"gün ve saat: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Sistem uygulamaları"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Çoklu görev"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Bölünmüş ekran"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Giriş"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Uygulama kısayolları"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Mevcut Uygulama"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Kısayolları özelleştirin"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Kısayol kaldırılsın mı?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Varsayılan kısayollara sıfırlansın mı?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Bu kısayolu oluşturmak için Eylem tuşuna ve bir veya daha fazla başka tuşa aynı anda basın"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Bu işlem, özel kısayolunuzu kalıcı olarak siler."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Bu işlem, tüm özel kısayollarınızı kalıcı olarak siler."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Kısayollarda ara"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sıfırla"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"İptal"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tuşa basın"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Tuş kombinasyonu zaten kullanılıyor. Başka bir tuş kombinasyonu deneyin."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Kısayol ayarlanamıyor."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Kısayol ekle"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Geri dön"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ana sayfaya git"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Son uygulamaları görüntüle"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Uygulamalar arasında geçiş yapma"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Bitti"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Tekrar deneyin."</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri dön"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Tebrikler!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Son uygulamaları görüntüleme hareketini tamamladınız."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Son kullanılan uygulamaları görüntülemek için dokunmatik alanda üç parmağınızla yukarı kaydırıp basılı tutun"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Uygulamalar arasında geçiş yapma"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Tebrikler!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Uygulamalar arasında geçiş yapma hareketini tamamladınız."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Tüm uygulamaları göster"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klavyenizde eylem tuşuna basın"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index e7e145f339c0..bd877f3a4c7c 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Оновлення"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Робочий профіль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим польоту"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Наступний сигнал о <xliff:g id="WHEN">%1$s</xliff:g> не пролунає"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системні додатки"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Багатозадачність"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Розділити екран"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Введення"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Комбінації клавіш для додатків"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Поточний додаток"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Налаштуйте комбінації клавіш"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Видалити комбінацію клавіш?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Відновити комбінації клавіш за умовчанням?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Щоб створити цю комбінацію, одночасно натисніть клавішу дії і одну чи кілька інших клавіш"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Вашу власну комбінацію клавіш буде видалено назавжди."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Усі ваші власні комбінації клавіш буде видалено назавжди."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пошук комбінацій клавіш"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Так, відновити"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Скасувати"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Натисніть клавішу"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Комбінація клавіш уже використовується. Спробуйте іншу."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Не вдалося встановити комбінацію клавіш."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Додати комбінацію клавіш"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Перейти на головний екран"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Переглянути нещодавні додатки"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Перемикання між додатками"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Спробуйте ще"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Чудово!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ви виконали жест для перегляду нещодавно відкритих додатків."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Щоб переглянути останні додатки, проведіть трьома пальцями вгору й утримуйте їх на сенсорній панелі"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Перемикання між додатками"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Чудово!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Ви виконали жест перемикання додатків."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Переглянути всі додатки"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Натисніть клавішу дії на клавіатурі"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 344c3911c144..d3900f2be29f 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"اپ ڈیٹ ہو رہا ہے"</string>
<string name="status_bar_work" msgid="5238641949837091056">"دفتری پروفائل"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ہوائی جہاز وضع"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"آپ کو <xliff:g id="WHEN">%1$s</xliff:g> بجے اپنا اگلا الارم سنائی نہیں دے گا"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> بجے"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> بجے"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"سسٹم ایپس"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ملٹی ٹاسکنگ"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"اسپلٹ اسکرین"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"ان پٹ"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ایپ شارٹ کٹس"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"موجودہ ایپ"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"شارٹ کٹس کو حسب ضرورت بنائیں"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"شارٹ کٹ ہٹائیں؟"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ڈیفالٹ پر واپس ری سیٹ کریں؟"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"یہ شارٹ کٹ تخلیق کرنے کے لیے، ایکشن کلید اور ایک یا زیادہ دوسری کلیدوں کو ایک ساتھ دبائیں"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"اس سے آپ کا حسب ضرورت شارٹ کٹ مستقل طور پر حذف ہو جائے گا۔"</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"اس سے آپ کے تمام حسب ضرورت شارٹ کٹس مستقل طور پر حذف ہو جائیں گے۔"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"تلاش کے شارٹ کٹس"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ہاں، ری سیٹ کریں"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"منسوخ کریں"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"کلید کو دبائیں"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"کلیدی مجموعہ پہلے سے استعمال میں ہے۔ دوسرا مجموعہ آزمائیں۔"</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"شارٹ کٹ سیٹ نہیں کیا جا سکتا۔"</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"شارٹ کٹ شامل کریں"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"واپس جائیں"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ہوم پر جائیں"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"حالیہ ایپس دیکھیں"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"ایپس سوئچ کریں"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ہو گیا"</string>
<string name="gesture_error_title" msgid="469064941635578511">"دوبارہ کوشش کریں!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"واپس جائیں"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"بہترین!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"آپ نے حالیہ ایپس دیکھیں کا اشارہ مکمل کر لیا ہے۔"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"حالیہ ایپس دیکھنے کے لیے، اپنے ٹچ پیڈ پر تین انگلیوں کی مدد سے اوپر کی طرف سوائپ کریں اور دبائے رکھیں"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"ایپس سوئچ کریں"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"بہترین!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"آپ نے ایپس کے مابین سوئچ کرنے کا اشارہ مکمل کر لیا۔"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"سبھی ایپس دیکھیں"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"اپنے کی بورڈ پر ایکشن کلید دبائیں"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index c8f4c182003e..7006abb34b99 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Yangilanmoqda"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Ish profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Parvoz rejimi"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Keyingi signal (<xliff:g id="WHEN">%1$s</xliff:g>) chalinmaydi"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Tizim ilovalari"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multi-vazifalilik"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekranni ikkiga ajratish"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Kiritish"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Ilova yorliqlari"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Joriy ilova"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Yorliqlarni moslash"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Tezkor tugma olib tashlansinmi?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Asliga qaytarilsinmi?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Bu tezkor tugmani yaratish uchun Amal tugmasi va bir yoki bir nechta tugmalarni birgalikda bosing"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Bunda maxsus tezkor tugma butunlay oʻchirib tashlanadi."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Bunda barcha maxsus yorliqlaringiz butunlay oʻchirib tashlanadi."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tezkor tugmalar qidiruvi"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ha, asliga qaytarilsin"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Bekor qilish"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tugmani bosing"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Bu tugmalar birikmasi band. Boshqa birikmani sinang."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Buyruq sozlanmadi."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Yorliq yaratish"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Orqaga"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Boshiga qaytish"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Oxirgi ilovalarni koʻrish"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Ilovalarni almashtirish"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Tayyor"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Qayta urining!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Orqaga qaytish"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Barakalla!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Oxirgi ilovalarni koʻrish ishorasini tugalladingiz."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Oxirgi ochilgan ilovalarni koʻrish uchun sensorli panelda uchta barmoq bilan tepega surib, ushlab turing"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Ilovalarni almashtirish"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Barakalla!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Ilovalarni almashtirish darsini tamomladingiz."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Barcha ilovalarni koʻrish"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klaviaturadagi amal tugmasini bosing"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 3b03652a36eb..18857426db84 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Đang cập nhật"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Hồ sơ công việc"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Chế độ trên máy bay"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Bạn sẽ không nghe thấy chuông báo tiếp theo lúc <xliff:g id="WHEN">%1$s</xliff:g> của mình"</string>
<string name="alarm_template" msgid="2234991538018805736">"lúc <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"vào <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Ứng dụng hệ thống"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Đa nhiệm"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Chia đôi màn hình"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Phương thức nhập"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Phím tắt cho ứng dụng"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Ứng dụng hiện tại"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Tuỳ chỉnh phím tắt"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Xoá phím tắt?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Đặt lại về phím tắt mặc định?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Để tạo phím tắt này, hãy nhấn tổ hợp phím Hành động và một hoặc nhiều phím khác"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Thao tác này sẽ xoá vĩnh viễn phím tắt tuỳ chỉnh của bạn."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Thao tác này sẽ xoá vĩnh viễn mọi phím tắt tuỳ chỉnh của bạn."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tìm phím tắt"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Có, đặt lại"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Huỷ"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Nhấn phím"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Tổ hợp phím đã được sử dụng. Hãy thử một tổ hợp khác."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Không đặt được lối tắt."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Thêm phím tắt"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Quay lại"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Chuyển đến màn hình chính"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Xem các ứng dụng gần đây"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Chuyển đổi ứng dụng"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Xong"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Hãy thử lại!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Quay lại"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Tuyệt vời!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Bạn đã hoàn tất cử chỉ xem ứng dụng gần đây."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Để xem các ứng dụng gần đây, hãy dùng 3 ngón tay vuốt lên và giữ trên bàn di chuột"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Chuyển đổi ứng dụng"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Tuyệt vời!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Bạn đã thực hiện xong cử chỉ chuyển đổi ứng dụng."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Xem tất cả các ứng dụng"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Nhấn phím hành động trên bàn phím"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 296cf61a7221..53c825baab02 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -83,7 +83,7 @@
<string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"必须先解锁设备,然后才能保存屏幕截图"</string>
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"请再次尝试截屏"</string>
<string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"无法保存屏幕截图"</string>
- <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"此应用或您所在的单位不允许进行屏幕截图"</string>
+ <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"此应用或您所在的组织不允许截屏"</string>
<string name="screenshot_blocked_by_admin" msgid="5486757604822795797">"您的 IT 管理员已禁止截取屏幕截图"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"编辑"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"编辑屏幕截图"</string>
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"正在更新"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作资料"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飞行模式"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"您在<xliff:g id="WHEN">%1$s</xliff:g>将不会听到下次闹钟响铃"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"系统应用"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多任务处理"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分屏"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"输入"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"应用快捷键"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"当前应用"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"自定义快捷方式"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"要移除快捷键吗?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"要重置为默认快捷键吗?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"若要创建此快捷方式,请同时按下快捷操作按键以及一个或多个其他键"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"此操作会永久删除您的自定义快捷键。"</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"此操作会永久删除您的所有自定义快捷键。"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜索快捷键"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"是,重置"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按下按键"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"按键组合已被使用,请尝试其他组合。"</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"无法设置快捷方式。"</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"添加快捷方式"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"返回"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"前往主屏幕"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近用过的应用"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"切换应用"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
<string name="gesture_error_title" msgid="469064941635578511">"再试一次!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"太棒了!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"您已完成“查看最近用过的应用”的手势教程。"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"如需查看最近用过的应用,请用三根手指在触控板上向上滑动并按住"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"切换应用"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"太棒了!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"您完成了应用切换手势教程。"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有应用"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按键盘上的快捷操作按键"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 0776af084be2..aa539d699378 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"正在更新"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作設定檔"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飛行模式"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"你不會<xliff:g id="WHEN">%1$s</xliff:g>聽到鬧鐘"</string>
<string name="alarm_template" msgid="2234991538018805736">"在 <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"在<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"系統應用程式"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多工處理"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分割螢幕"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"輸入"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"應用程式捷徑"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"目前的應用程式"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"自訂快速鍵"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"要移除快速鍵嗎?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"要重設至預設捷徑嗎?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"如要建立此快速鍵,請同時按下 Action 鍵及另外一個或多個鍵"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"這將永久刪除你的自訂快速鍵。"</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"這將永久刪除你的所有自訂捷徑。"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"是,請重設"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按鍵"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"此按鍵組合已在使用,請改用其他組合。"</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"無法設定快速鍵。"</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"新增捷徑"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"返回"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"返回主畫面"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近使用的應用程式"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"切換應用程式"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
<string name="gesture_error_title" msgid="469064941635578511">"請再試一次!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"做得好!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"你已完成「查看最近使用的應用程式」手勢的教學課程。"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"如要查看最近使用的應用程式,請用三隻手指在觸控板上向上滑動並按住"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"切換應用程式"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"做得好!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"你已完成「應用程式切換手勢」的教學課程。"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有應用程式"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按下鍵盤上的快捷操作鍵"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index bf1f1921eb13..2d6ac8d43442 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -750,6 +750,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"更新中"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作資料夾"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飛航模式"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"你不會聽到下一個<xliff:g id="WHEN">%1$s</xliff:g> 的鬧鐘"</string>
<string name="alarm_template" msgid="2234991538018805736">"於<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"於<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1430,6 +1432,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"系統應用程式"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多工處理"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"分割畫面"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"輸入"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"應用程式捷徑"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"目前的應用程式"</string>
@@ -1438,8 +1442,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"自訂快速鍵"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"要移除快速鍵嗎?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"要重設為預設值嗎?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"如要建立這個快速鍵,請同時按下快捷操作鍵和一或多個其他鍵"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"這項操作會永久刪除自訂快速鍵。"</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"這麼做會永久刪除所有快速鍵。"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
@@ -1461,8 +1464,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"是,請重設"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按下按鍵"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"這組按鍵已在使用中,請試試其他組合。"</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"無法設定捷徑。"</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"新增快速鍵"</string>
@@ -1476,8 +1478,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"返回"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"返回主畫面"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近使用的應用程式"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"切換應用程式"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
<string name="gesture_error_title" msgid="469064941635578511">"請再試一次!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
@@ -1495,15 +1496,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"太棒了!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"你已完成「查看最近使用的應用程式」手勢教學課程。"</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"如要查看最近使用的應用程式,請在觸控板上用三指向上滑動並按住"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"切換應用程式"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"太棒了!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"你已完成應用程式切換手勢的教學課程。"</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有應用程式"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按下鍵盤上的快捷操作鍵"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 9f8fa879fe86..a210ca37214f 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -752,6 +752,8 @@
<string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Iyabuyekeza"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Iphrofayela yomsebenzi"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Imodi yendiza"</string>
+ <!-- no translation found for status_bar_supervision (6735015942701134125) -->
+ <skip />
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ngeke uzwe i-alamu yakho elandelayo ngo-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"ngo-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"nge-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -1432,6 +1434,8 @@
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Ama-app esistimu"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Ukwenza imisebenzi eminingi"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Hlukanisa isikrini"</string>
+ <!-- no translation found for shortcutHelper_category_accessibility (8068337792277570938) -->
+ <skip />
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Okokufaka"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Izinqamuleli Zohlelo lokusebenza"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"I-App yamanje"</string>
@@ -1440,8 +1444,7 @@
<string name="shortcut_helper_customize_mode_title" msgid="8327297960035006036">"Qamba ngokwabahlinzekelwayo izinqamuleli"</string>
<string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Susa isinqamuleli?"</string>
<string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Setha kabusha ubuyele kokuzenzakalelayo?"</string>
- <!-- no translation found for shortcut_customize_mode_add_shortcut_description (7636040209946696120) -->
- <skip />
+ <string name="shortcut_customize_mode_add_shortcut_description" msgid="7636040209946696120">"Ukuze usungule lesi sinqamuleli, cindezela ukhiye Wesenzo kanye nokhiye owodwa noma ngaphezulu ndawonye"</string>
<string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Lokhu kuzosula isinqamuleli sakho somuntu ngamunye unomphela."</string>
<string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Lokhu kuzosula unomphela zonke izinqamuleli zakho zangokwezifiso."</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sesha izinqamuleli"</string>
@@ -1463,8 +1466,7 @@
<string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Yebo, setha kabusha"</string>
<string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Khansela"</string>
<string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Cindezela ukhiye"</string>
- <!-- no translation found for shortcut_customizer_key_combination_in_use_error_message (3325858369539848162) -->
- <skip />
+ <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="3325858369539848162">"Inhlanganisela yokhiye isiyasetshenziswa kakade. Zama enye inhlanganisela."</string>
<string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Isinqamuleli asikwazi ukusethwa."</string>
<string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
<string name="shortcut_helper_add_shortcut_button_label" msgid="7655779534665954910">"Faka isinqamuleli"</string>
@@ -1478,8 +1480,7 @@
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Iya emuva"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Iya ekhasini lokuqala"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Buka ama-app akamuva"</string>
- <!-- no translation found for touchpad_tutorial_switch_apps_gesture_button (7768255095423767779) -->
- <skip />
+ <string name="touchpad_tutorial_switch_apps_gesture_button" msgid="7768255095423767779">"Shintsha ama-app"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Kwenziwe"</string>
<string name="gesture_error_title" msgid="469064941635578511">"Zama futhi!"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Buyela emuva"</string>
@@ -1497,15 +1498,12 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Umsebenzi omuhle!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Uqedele ukubuka ukuthinta kwama-app akamuva."</string>
<string name="touchpad_recent_gesture_error_body" msgid="8695535720378462022">"Ukuze ubuke ama-app akamuva, swayiphela phezulu futhi ubambe usebenzisa iminwe emithathu kuphedi yakho yokuthinta"</string>
- <!-- no translation found for touchpad_switch_apps_gesture_action_title (6835222344612924512) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_guidance (6859830005390732562) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_title (4894947244328032458) -->
- <skip />
- <!-- no translation found for touchpad_switch_apps_gesture_success_body (8151089866035126312) -->
+ <string name="touchpad_switch_apps_gesture_action_title" msgid="6835222344612924512">"Shintsha ama-app"</string>
+ <!-- no translation found for touchpad_switch_apps_gesture_guidance (2751565200937541667) -->
<skip />
- <!-- no translation found for touchpad_switch_gesture_error_body (2211950382592343759) -->
+ <string name="touchpad_switch_apps_gesture_success_title" msgid="4894947244328032458">"Umsebenzi omuhle!"</string>
+ <string name="touchpad_switch_apps_gesture_success_body" msgid="8151089866035126312">"Ukuqedile ukuthinta kokushintsha ama-app."</string>
+ <!-- no translation found for touchpad_switch_gesture_error_body (5508381152326379652) -->
<skip />
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Buka wonke ama-app"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Cindezela inkinobho yokufinyelela kukhibhodi yakho"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d2b7d0b90c43..36ede64f91d9 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -31,8 +31,14 @@
<!-- The dark background color behind the shade -->
<color name="shade_scrim_background_dark">@androidprv:color/system_under_surface_light</color>
- <!-- Colors for notification shade/scrim -->
- <color name="shade_panel">@android:color/system_accent1_800</color>
+ <!-- Base colors for notification shade/scrim, the alpha component is adjusted programmatically
+ to match the spec -->
+ <color name="shade_panel">@android:color/system_accent1_900</color>
+ <color name="surface_effect_0">@android:color/system_accent1_100</color>
+
+ <!-- todo(b/388891904) Remove updated color references once they are available. -->
+ <color name="shade_panel_base">@color/shade_panel</color>
+ <color name="notification_scrim_base">@color/surface_effect_0</color>
<!-- The color of the background in the separated list of the Global Actions menu -->
<color name="global_actions_separated_background">#F5F5F5</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f9904e336f24..449e4bb46027 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -266,6 +266,9 @@
<!-- Height of a large notification in the status bar -->
<dimen name="notification_max_height">358dp</dimen>
+ <!-- Height of a large promoted ongoing notification in the status bar -->
+ <dimen name="notification_max_height_for_promoted_ongoing">218dp</dimen>
+
<!-- Height of a heads up notification in the status bar for legacy custom views -->
<dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
@@ -2057,6 +2060,8 @@
<dimen name="dream_overlay_status_bar_ambient_text_shadow_dy">0.5dp</dimen>
<dimen name="dream_overlay_status_bar_ambient_text_shadow_radius">3dp</dimen>
<dimen name="dream_overlay_icon_inset_dimen">0dp</dimen>
+ <dimen name="dream_overlay_icon_shadow_radius">1dp</dimen>
+ <dimen name="dream_overlay_icon_ambient_shadow_radius">2dp</dimen>
<!-- Default device corner radius, used for assist UI -->
<dimen name="config_rounded_mask_size">0px</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 79fa41949b57..1e52e9f135cb 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1351,14 +1351,14 @@
<string name="communal_widgets_disclaimer_text">To open an app using a widget, you\u2019ll need to verify it\u2019s you. Also, keep in mind that anyone can view them, even when your tablet\u2019s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here.</string>
<!-- Button for user to verify they understand the information presented. [CHAR LIMIT=50] -->
<string name="communal_widgets_disclaimer_button">Got it</string>
- <!-- Label for a lock screen affordance to show widgets on the lock screen. [CHAR LIMIT=20] -->
- <string name="glanceable_hub_lockscreen_affordance_label">Widgets</string>
- <!-- Text explaining why the lock screen affordance to show widgets on the lockscreen is disabled and how to enable the affordance in settings. [CHAR LIMIT=NONE] -->
- <string name="glanceable_hub_lockscreen_affordance_disabled_text">To add the \"Widgets\" shortcut, make sure \"Show widgets on lock screen\" is enabled in settings.</string>
- <!-- Label for a button used to open Settings in order to enable showing widgets on the lock screen. [CHAR LIMIT=NONE] -->
- <string name="glanceable_hub_lockscreen_affordance_action_button_label">Settings</string>
<!-- Content description for a "show screensaver" button on glanceable hub. [CHAR LIMIT=NONE] -->
<string name="accessibility_glanceable_hub_to_dream_button">Show screensaver button</string>
+ <!-- Title shown in hub onboarding bottom sheet. [CHAR LIMIT=50] -->
+ <string name="hub_onboarding_bottom_sheet_title">Explore hub mode</string>
+ <!-- Information about communal hub shown in the onboarding bottom sheet. [CHAR LIMIT=NONE] -->
+ <string name="hub_onboarding_bottom_sheet_text">Access your favorite widgets and screen savers while charging.</string>
+ <!-- Hub onboarding bottom sheet action button title. [CHAR LIMIT=NONE] -->
+ <string name="hub_onboarding_bottom_sheet_action_button">Let\u2019s go</string>
<!-- Related to user switcher --><skip/>
@@ -4005,13 +4005,13 @@
<!-- Touchpad switch apps gesture action name in tutorial [CHAR LIMIT=NONE] -->
<string name="touchpad_switch_apps_gesture_action_title">Switch apps</string>
<!-- Touchpad switch apps gesture guidance in gestures tutorial [CHAR LIMIT=NONE] -->
- <string name="touchpad_switch_apps_gesture_guidance">Swipe left using four fingers on your touchpad</string>
+ <string name="touchpad_switch_apps_gesture_guidance">Swipe right using four fingers on your touchpad</string>
<!-- Screen title after switch apps gesture was done successfully [CHAR LIMIT=NONE] -->
<string name="touchpad_switch_apps_gesture_success_title">Great job!</string>
<!-- Text shown to the user after they complete switch apps gesture tutorial [CHAR LIMIT=NONE] -->
<string name="touchpad_switch_apps_gesture_success_body">You completed the switch apps gesture.</string>
<!-- Text shown to the user after switch gesture was not done correctly [CHAR LIMIT=NONE] -->
- <string name="touchpad_switch_gesture_error_body">Swipe left using four fingers on your touchpad to switch apps</string>
+ <string name="touchpad_switch_gesture_error_body">Swipe right using four fingers on your touchpad to switch apps</string>
<!-- KEYBOARD TUTORIAL-->
<!-- Action key tutorial title [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 08891aa65417..b0d9bed05e27 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -430,6 +430,7 @@
<style name="Theme.SystemUI.MediaProjectionAppSelector"
parent="@*android:style/Theme.DeviceDefault.Chooser">
+ <item name="android:colorBackground">@*android:color/materialColorSurfaceContainer</item>
</style>
<!-- Standard animations for hiding and showing the status bar. -->
diff --git a/packages/SystemUI/res/xml/gradient_color_wallpaper.xml b/packages/SystemUI/res/xml/gradient_color_wallpaper.xml
new file mode 100644
index 000000000000..f453a4450750
--- /dev/null
+++ b/packages/SystemUI/res/xml/gradient_color_wallpaper.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<wallpaper
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:showMetadataInPreview="false"
+ android:supportsMultipleDisplays="true" /> \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
index bd20777c7102..e928525afc14 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shared.shadow
import android.content.Context
+import android.content.res.TypedArray
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.util.AttributeSet
@@ -31,19 +32,23 @@ constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
- defStyleRes: Int = 0
+ defStyleRes: Int = 0,
) : TextView(context, attrs, defStyleAttr, defStyleRes) {
- private val mKeyShadowInfo: ShadowInfo
- private val mAmbientShadowInfo: ShadowInfo
+ private lateinit var mKeyShadowInfo: ShadowInfo
+ private lateinit var mAmbientShadowInfo: ShadowInfo
init {
- val attributes =
+ updateShadowDrawables(
context.obtainStyledAttributes(
attrs,
R.styleable.DoubleShadowTextView,
defStyleAttr,
- defStyleRes
+ defStyleRes,
)
+ )
+ }
+
+ private fun updateShadowDrawables(attributes: TypedArray) {
val drawableSize: Int
val drawableInsetSize: Int
try {
@@ -70,17 +75,17 @@ constructor(
ambientShadowBlur,
ambientShadowOffsetX,
ambientShadowOffsetY,
- ambientShadowAlpha
+ ambientShadowAlpha,
)
drawableSize =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextView_drawableIconSize,
- 0
+ 0,
)
drawableInsetSize =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextView_drawableIconInsetSize,
- 0
+ 0,
)
} finally {
attributes.recycle()
@@ -95,12 +100,19 @@ constructor(
mAmbientShadowInfo,
drawable,
drawableSize,
- drawableInsetSize
+ drawableInsetSize,
)
}
setCompoundDrawablesRelative(drawables[0], drawables[1], drawables[2], drawables[3])
}
+ override fun setTextAppearance(resId: Int) {
+ super.setTextAppearance(resId)
+ updateShadowDrawables(
+ context.obtainStyledAttributes(resId, R.styleable.DoubleShadowTextView)
+ )
+ }
+
public override fun onDraw(canvas: Canvas) {
applyShadows(mKeyShadowInfo, mAmbientShadowInfo, this, canvas) { super.onDraw(canvas) }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
index 953cf88feccb..943cbe87c8c2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
@@ -49,6 +49,7 @@ data class KeyguardFingerprintListenModel(
var systemUser: Boolean = false,
var udfps: Boolean = false,
var userDoesNotHaveTrust: Boolean = false,
+ var communalShowing: Boolean = false,
) : KeyguardListenModel() {
/** List of [String] to be used as a [Row] with [DumpsysTableLogger]. */
@@ -81,6 +82,7 @@ data class KeyguardFingerprintListenModel(
systemUser.toString(),
udfps.toString(),
userDoesNotHaveTrust.toString(),
+ communalShowing.toString(),
)
}
@@ -122,6 +124,7 @@ data class KeyguardFingerprintListenModel(
systemUser = model.systemUser
udfps = model.udfps
userDoesNotHaveTrust = model.userDoesNotHaveTrust
+ communalShowing = model.communalShowing
}
}
@@ -170,6 +173,7 @@ data class KeyguardFingerprintListenModel(
"systemUser",
"underDisplayFingerprint",
"userDoesNotHaveTrust",
+ "communalShowing",
)
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 8cfb4c5592aa..ff7b2b025539 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -1242,7 +1242,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
public void reinflateViewFlipper(
KeyguardSecurityViewFlipperController.OnViewInflatedCallback onViewInflatedListener) {
mSecurityViewFlipperController.clearViews();
- mSecurityViewFlipperController.asynchronouslyInflateView(mCurrentSecurityMode,
+ mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode,
mKeyguardSecurityCallback, (controller) -> {
mView.updateSecurityViewFlipper();
onViewInflatedListener.onViewInflated(controller);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index 120045fc058b..641cac51785f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -23,7 +23,6 @@ import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
import android.util.Log;
import android.view.LayoutInflater;
-import androidx.annotation.Nullable;
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
import com.android.internal.annotations.VisibleForTesting;
@@ -35,7 +34,9 @@ import com.android.systemui.res.R;
import com.android.systemui.util.ViewController;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import javax.inject.Inject;
@@ -56,6 +57,8 @@ public class KeyguardSecurityViewFlipperController
private final EmergencyButtonController.Factory mEmergencyButtonControllerFactory;
private final Factory mKeyguardSecurityViewControllerFactory;
private final FeatureFlags mFeatureFlags;
+ private final List<OnViewInflatedCallback> mOnViewInflatedListeners = new ArrayList<>();
+ private final Set<SecurityMode> mSecurityModeInProgress = new HashSet<>();
@Inject
protected KeyguardSecurityViewFlipperController(KeyguardSecurityViewFlipper view,
@@ -106,7 +109,13 @@ public class KeyguardSecurityViewFlipperController
}
}
- asynchronouslyInflateView(securityMode, keyguardSecurityCallback, onViewInflatedCallback);
+ // Prevent multiple inflations for the same security mode. Instead, add callback to a list
+ // and then notify each in order when the view is inflated.
+ mOnViewInflatedListeners.add(onViewInflatedCallback);
+ if (!mSecurityModeInProgress.contains(securityMode)) {
+ mSecurityModeInProgress.add(securityMode);
+ asynchronouslyInflateView(securityMode, keyguardSecurityCallback);
+ }
}
/**
@@ -117,9 +126,8 @@ public class KeyguardSecurityViewFlipperController
* @param securityMode
* @param keyguardSecurityCallback
*/
- public void asynchronouslyInflateView(SecurityMode securityMode,
- KeyguardSecurityCallback keyguardSecurityCallback,
- @Nullable OnViewInflatedCallback onViewInflatedListener) {
+ private void asynchronouslyInflateView(SecurityMode securityMode,
+ KeyguardSecurityCallback keyguardSecurityCallback) {
int layoutId = mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE)
? getLayoutIdFor(securityMode) : getLegacyLayoutIdFor(securityMode);
if (layoutId != 0) {
@@ -129,24 +137,26 @@ public class KeyguardSecurityViewFlipperController
mAsyncLayoutInflater.inflate(layoutId, mView,
(view, resId, parent) -> {
mView.addView(view);
+ mSecurityModeInProgress.remove(securityMode);
KeyguardInputViewController<KeyguardInputView> childController =
mKeyguardSecurityViewControllerFactory.create(
(KeyguardInputView) view,
securityMode, keyguardSecurityCallback);
childController.init();
mChildren.add(childController);
- if (onViewInflatedListener != null) {
- onViewInflatedListener.onViewInflated(childController);
- // Single bouncer constrains are default
- if (mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE)) {
- boolean useSplitBouncer =
- getResources().getBoolean(R.bool.update_bouncer_constraints)
- && getResources().getConfiguration().orientation
- == ORIENTATION_LANDSCAPE;
+ for (OnViewInflatedCallback callback : mOnViewInflatedListeners) {
+ callback.onViewInflated(childController);
+ }
+ mOnViewInflatedListeners.clear();
- updateConstraints(useSplitBouncer);
- }
+ // Single bouncer constrains are default
+ if (mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE)) {
+ boolean useSplitBouncer =
+ getResources().getBoolean(R.bool.update_bouncer_constraints)
+ && getResources().getConfiguration().orientation
+ == ORIENTATION_LANDSCAPE;
+ updateConstraints(useSplitBouncer);
}
});
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 101fcdfce7e5..c266a5b47cff 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -43,6 +43,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+import static com.android.systemui.Flags.glanceableHubV2;
import static com.android.systemui.Flags.simPinBouncerReset;
import static com.android.systemui.Flags.simPinUseSlotId;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
@@ -128,6 +129,7 @@ import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -294,6 +296,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final Provider<JavaAdapter> mJavaAdapter;
private final Provider<SceneInteractor> mSceneInteractor;
private final Provider<AlternateBouncerInteractor> mAlternateBouncerInteractor;
+ private final CommunalSceneInteractor mCommunalSceneInteractor;
private final AuthController mAuthController;
private final UiEventLogger mUiEventLogger;
private final Set<String> mAllowFingerprintOnOccludingActivitiesFromPackage;
@@ -404,6 +407,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
protected int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
private boolean mFingerprintDetectRunning;
private boolean mIsDreaming;
+ private boolean mCommunalShowing;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private final FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
@@ -2205,7 +2209,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
IActivityTaskManager activityTaskManagerService,
Provider<AlternateBouncerInteractor> alternateBouncerInteractor,
Provider<JavaAdapter> javaAdapter,
- Provider<SceneInteractor> sceneInteractor) {
+ Provider<SceneInteractor> sceneInteractor,
+ CommunalSceneInteractor communalSceneInteractor) {
mContext = context;
mSubscriptionManager = subscriptionManager;
mUserTracker = userTracker;
@@ -2254,6 +2259,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mAlternateBouncerInteractor = alternateBouncerInteractor;
mJavaAdapter = javaAdapter;
mSceneInteractor = sceneInteractor;
+ mCommunalSceneInteractor = communalSceneInteractor;
mHandler = new Handler(mainLooper) {
@Override
@@ -2535,6 +2541,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
);
}
+ if (glanceableHubV2()) {
+ mJavaAdapter.get().alwaysCollectFlow(
+ mCommunalSceneInteractor.isCommunalVisible(),
+ this::onCommunalShowingChanged
+ );
+ }
+
// start() can be invoked in the middle of user switching, so check for this state and issue
// the call manually as that important event was missed.
if (mUserTracker.isUserSwitching()) {
@@ -2837,6 +2850,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
/**
+ * Sets whether the communal hub is showing.
+ */
+ @VisibleForTesting
+ void onCommunalShowingChanged(boolean showing) {
+ mCommunalShowing = showing;
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
+ }
+
+ /**
* Whether the alternate bouncer is showing.
*/
public void setAlternateBouncerShowing(boolean showing) {
@@ -2998,11 +3020,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
final boolean shouldListenBouncerState =
!strongerAuthRequired || !isPrimaryBouncerShowingOrWillBeShowing();
+ final boolean isUdfpsAuthRequiredOnCommunal =
+ !mCommunalShowing || isAlternateBouncerShowing();
final boolean shouldListenUdfpsState = !isUdfps
|| (!userCanSkipBouncer
&& !strongerAuthRequired
- && userDoesNotHaveTrust);
+ && userDoesNotHaveTrust
+ && (!glanceableHubV2() || isUdfpsAuthRequiredOnCommunal));
boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
@@ -3033,7 +3058,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mSwitchingUser,
mIsSystemUser,
isUdfps,
- userDoesNotHaveTrust));
+ userDoesNotHaveTrust,
+ mCommunalShowing));
return shouldListen;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
index e7470a34a065..102efcf7badd 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility.floatingmenu;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
import android.content.Context;
@@ -90,9 +91,11 @@ class MenuViewLayerController implements IAccessibilityFloatingMenu {
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
+ params.setTitle("FloatingMenu");
params.receiveInsetsIgnoringZOrder = true;
params.privateFlags |=
- PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION | SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION | SYSTEM_FLAG_SHOW_FOR_ALL_USERS
+ | PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION;
params.windowAnimations = android.R.style.Animation_Translucent;
// Insets are configured to allow the menu to display over navigation and system bars.
params.setFitInsetsTypes(0);
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
index 081d2a087b07..b7b31566a5e9 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
@@ -75,8 +75,8 @@ public class AmbientStatusBarView extends ConstraintLayout {
private ShadowInfo mAmbientShadowInfo;
private int mDrawableSize;
private int mDrawableInsetSize;
- private static final float KEY_SHADOW_ALPHA = 0.8f;
- private static final float AMBIENT_SHADOW_ALPHA = 0.6f;
+ private static final float KEY_SHADOW_ALPHA = 0.9f;
+ private static final float AMBIENT_SHADOW_ALPHA = 0.7f;
public AmbientStatusBarView(Context context) {
this(context, null);
@@ -102,14 +102,14 @@ public class AmbientStatusBarView extends ConstraintLayout {
super.onFinishInflate();
mKeyShadowInfo = createShadowInfo(
- R.dimen.dream_overlay_status_bar_key_text_shadow_radius,
+ R.dimen.dream_overlay_icon_shadow_radius,
R.dimen.dream_overlay_status_bar_key_text_shadow_dx,
R.dimen.dream_overlay_status_bar_key_text_shadow_dy,
KEY_SHADOW_ALPHA
);
mAmbientShadowInfo = createShadowInfo(
- R.dimen.dream_overlay_status_bar_ambient_text_shadow_radius,
+ R.dimen.dream_overlay_icon_ambient_shadow_radius,
R.dimen.dream_overlay_status_bar_ambient_text_shadow_dx,
R.dimen.dream_overlay_status_bar_ambient_text_shadow_dy,
AMBIENT_SHADOW_ALPHA
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/colors/ShadeColors.kt b/packages/SystemUI/src/com/android/systemui/common/shared/colors/ShadeColors.kt
new file mode 100644
index 000000000000..70ace6a6d038
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/colors/ShadeColors.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.common.shared.colors
+
+import android.content.res.Resources
+import android.graphics.Color
+import com.android.internal.graphics.ColorUtils
+import com.android.systemui.res.R
+
+object ShadeColors {
+ @JvmStatic
+ fun Resources.shadeBasePanel(): Int {
+ val layerAbove =
+ ColorUtils.setAlphaComponent(getColor(R.color.shade_panel), (0.4f * 255).toInt())
+ val layerBelow = ColorUtils.setAlphaComponent(Color.WHITE, (0.1f * 255).toInt())
+ return ColorUtils.compositeColors(layerAbove, layerBelow)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/colors/SurfaceEffectColors.kt b/packages/SystemUI/src/com/android/systemui/common/shared/colors/SurfaceEffectColors.kt
new file mode 100644
index 000000000000..d4027c029a0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/colors/SurfaceEffectColors.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.common.shared.colors
+
+import android.content.res.Resources
+import android.graphics.Color
+import com.android.internal.graphics.ColorUtils
+import com.android.systemui.res.R
+
+object SurfaceEffectColors {
+ @JvmStatic
+ fun Resources.surfaceEffect0(): Int {
+ return getColor(com.android.internal.R.color.surface_effect_0)
+ }
+
+ @JvmStatic
+ fun Resources.surfaceEffect1(): Int {
+ return getColor(com.android.internal.R.color.surface_effect_1)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
index 7fcdd9596049..5671fa49c716 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
@@ -19,15 +19,16 @@ package com.android.systemui.common.ui.data.repository
import android.content.Context
import android.content.res.Configuration
+import android.view.Display
import android.view.DisplayInfo
import androidx.annotation.DimenRes
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.wrapper.DisplayUtilsWrapper
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -57,7 +58,7 @@ interface ConfigurationRepository {
val configurationValues: Flow<Configuration>
/** Emits the latest display this configuration controller has been moved to. */
- val onMovedToDisplay: Flow<Int>
+ val onMovedToDisplay: StateFlow<Int>
fun getResolutionScale(): Float
@@ -121,20 +122,21 @@ constructor(
configurationController.addCallback(callback)
awaitClose { configurationController.removeCallback(callback) }
}
- override val onMovedToDisplay: Flow<Int>
- get() = conflatedCallbackFlow {
- val callback =
- object : ConfigurationController.ConfigurationListener {
- override fun onMovedToDisplay(
- newDisplayId: Int,
- newConfiguration: Configuration?,
- ) {
- trySend(newDisplayId)
+ override val onMovedToDisplay: StateFlow<Int> =
+ conflatedCallbackFlow {
+ val callback =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onMovedToDisplay(
+ newDisplayId: Int,
+ newConfiguration: Configuration?,
+ ) {
+ trySend(newDisplayId)
+ }
}
- }
- configurationController.addCallback(callback)
- awaitClose { configurationController.removeCallback(callback) }
- }
+ configurationController.addCallback(callback)
+ awaitClose { configurationController.removeCallback(callback) }
+ }
+ .stateIn(scope, SharingStarted.Eagerly, Display.DEFAULT_DISPLAY)
override val scaleForResolution: StateFlow<Float> =
onConfigurationChange
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingCommandListener.kt b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingCommandListener.kt
deleted file mode 100644
index c7b7050340a0..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingCommandListener.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.communal
-
-import android.annotation.SuppressLint
-import android.app.DreamManager
-import com.android.systemui.CoreStartable
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.commandline.Command
-import com.android.systemui.statusbar.commandline.CommandRegistry
-import java.io.PrintWriter
-import javax.inject.Inject
-
-@SysUISingleton
-class DevicePosturingCommandListener
-@Inject
-constructor(private val commandRegistry: CommandRegistry, private val dreamManager: DreamManager) :
- CoreStartable {
- private val command = DevicePosturingCommand()
-
- override fun start() {
- commandRegistry.registerCommand(COMMAND_ROOT) { command }
- }
-
- internal inner class DevicePosturingCommand : Command {
- @SuppressLint("MissingPermission")
- override fun execute(pw: PrintWriter, args: List<String>) {
- val arg = args.getOrNull(0)
- if (arg == null || arg.lowercase() == "help") {
- help(pw)
- return
- }
-
- when (arg.lowercase()) {
- "true" -> dreamManager.setDevicePostured(true)
- "false" -> dreamManager.setDevicePostured(false)
- else -> {
- pw.println("Invalid argument!")
- help(pw)
- }
- }
- }
-
- override fun help(pw: PrintWriter) {
- pw.println("Usage: $ adb shell cmd statusbar device-postured <true|false>")
- }
- }
-
- private companion object {
- const val COMMAND_ROOT = "device-postured"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
new file mode 100644
index 000000000000..47040fa4a572
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal
+
+import android.annotation.SuppressLint
+import android.app.DreamManager
+import android.service.dreams.Flags.allowDreamWhenPostured
+import com.android.app.tracing.coroutines.launchInTraced
+import com.android.systemui.CoreStartable
+import com.android.systemui.communal.posturing.domain.interactor.PosturingInteractor
+import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.dagger.CommunalTableLog
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.onEach
+
+@SysUISingleton
+class DevicePosturingListener
+@Inject
+constructor(
+ private val commandRegistry: CommandRegistry,
+ private val dreamManager: DreamManager,
+ private val interactor: PosturingInteractor,
+ @Background private val bgScope: CoroutineScope,
+ @CommunalTableLog private val tableLogBuffer: TableLogBuffer,
+) : CoreStartable {
+ private val command = DevicePosturingCommand()
+
+ @SuppressLint("MissingPermission")
+ override fun start() {
+ if (!allowDreamWhenPostured()) {
+ return
+ }
+
+ interactor.postured
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnPrefix = "",
+ columnName = "postured",
+ initialValue = false,
+ )
+ .onEach { postured -> dreamManager.setDevicePostured(postured) }
+ .launchInTraced("$TAG#collectPostured", bgScope)
+
+ commandRegistry.registerCommand(COMMAND_ROOT) { command }
+ }
+
+ internal inner class DevicePosturingCommand : Command {
+ @SuppressLint("MissingPermission")
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ val arg = args.getOrNull(0)
+ if (arg == null || arg.lowercase() == "help") {
+ help(pw)
+ return
+ }
+
+ val state =
+ when (arg.lowercase()) {
+ "true" -> PosturedState.Postured(confidence = 1f)
+ "false" -> PosturedState.NotPostured
+ "clear" -> PosturedState.Unknown
+ else -> {
+ pw.println("Invalid argument!")
+ help(pw)
+ null
+ }
+ }
+ state?.let { interactor.setValueForDebug(it) }
+ }
+
+ override fun help(pw: PrintWriter) {
+ pw.println("Usage: $ adb shell cmd statusbar device-postured <true|false|clear>")
+ }
+ }
+
+ private companion object {
+ const val COMMAND_ROOT = "device-postured"
+ const val TAG = "DevicePosturingListener"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
index e3443227685f..7358aa7b3fcd 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
@@ -22,7 +22,7 @@ import com.android.systemui.communal.CommunalDreamStartable
import com.android.systemui.communal.CommunalMetricsStartable
import com.android.systemui.communal.CommunalOngoingContentStartable
import com.android.systemui.communal.CommunalSceneStartable
-import com.android.systemui.communal.DevicePosturingCommandListener
+import com.android.systemui.communal.DevicePosturingListener
import com.android.systemui.communal.log.CommunalLoggerStartable
import com.android.systemui.communal.widgets.CommunalAppWidgetHostStartable
import com.android.systemui.dagger.qualifiers.PerUser
@@ -71,6 +71,6 @@ interface CommunalStartableModule {
@Binds
@IntoMap
- @ClassKey(DevicePosturingCommandListener::class)
- fun bindDevicePosturingCommandListener(impl: DevicePosturingCommandListener): CoreStartable
+ @ClassKey(DevicePosturingListener::class)
+ fun bindDevicePosturingistener(impl: DevicePosturingListener): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
index 4de39c457f3b..a02bc8b89910 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
@@ -51,6 +51,12 @@ interface CommunalPrefsRepository {
/** Save the CTA tile dismissed state for the current user. */
suspend fun setCtaDismissed(user: UserInfo)
+
+ /** Whether hub onboarding has been dismissed. */
+ fun isHubOnboardingDismissed(user: UserInfo): Flow<Boolean>
+
+ /** Save the hub onboarding dismissed state for the current user. */
+ suspend fun setHubOnboardingDismissed(user: UserInfo)
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -65,9 +71,6 @@ constructor(
) : CommunalPrefsRepository {
private val logger by lazy { Logger(logBuffer, TAG) }
- override fun isCtaDismissed(user: UserInfo): Flow<Boolean> =
- readKeyForUser(user, CTA_DISMISSED_STATE)
-
/**
* Emits an event each time a Backup & Restore restoration job is completed, and once at the
* start of collection.
@@ -82,18 +85,29 @@ constructor(
.onEach { logger.i("Restored state for communal preferences.") }
.emitOnStart()
+ override fun isCtaDismissed(user: UserInfo): Flow<Boolean> =
+ readKeyForUser(user, CTA_DISMISSED_STATE)
+
override suspend fun setCtaDismissed(user: UserInfo) =
withContext(bgDispatcher) {
getSharedPrefsForUser(user).edit().putBoolean(CTA_DISMISSED_STATE, true).apply()
logger.i("Dismissed CTA tile")
}
+ override fun isHubOnboardingDismissed(user: UserInfo): Flow<Boolean> =
+ readKeyForUser(user, HUB_ONBOARDING_DISMISSED_STATE)
+
+ override suspend fun setHubOnboardingDismissed(user: UserInfo) =
+ withContext(bgDispatcher) {
+ getSharedPrefsForUser(user)
+ .edit()
+ .putBoolean(HUB_ONBOARDING_DISMISSED_STATE, true)
+ .apply()
+ logger.i("Dismissed hub onboarding")
+ }
+
private fun getSharedPrefsForUser(user: UserInfo): SharedPreferences {
- return userFileManager.getSharedPreferences(
- FILE_NAME,
- Context.MODE_PRIVATE,
- user.id,
- )
+ return userFileManager.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE, user.id)
}
private fun readKeyForUser(user: UserInfo, key: String): Flow<Boolean> {
@@ -109,5 +123,6 @@ constructor(
const val TAG = "CommunalPrefsRepository"
const val FILE_NAME = "communal_hub_prefs"
const val CTA_DISMISSED_STATE = "cta_dismissed"
+ const val HUB_ONBOARDING_DISMISSED_STATE = "hub_onboarding_dismissed"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt
index 0b5f40d8041e..76e6cde4ad81 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt
@@ -32,6 +32,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
@@ -63,6 +64,24 @@ constructor(
suspend fun setCtaDismissed(user: UserInfo = userTracker.userInfo) =
repository.setCtaDismissed(user)
+ val isHubOnboardingDismissed: Flow<Boolean> =
+ userInteractor.selectedUserInfo
+ .flatMapLatest { user -> repository.isHubOnboardingDismissed(user) }
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnPrefix = "",
+ columnName = "isHubOnboardingDismissed",
+ initialValue = false,
+ )
+ .stateIn(
+ scope = bgScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ fun setHubOnboardingDismissed(user: UserInfo = userTracker.userInfo) =
+ bgScope.launch { repository.setHubOnboardingDismissed(user) }
+
private companion object {
const val TAG = "CommunalPrefsInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/HubOnboardingInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/HubOnboardingInteractor.kt
new file mode 100644
index 000000000000..26a0a7930b4a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/HubOnboardingInteractor.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.domain.interactor
+
+import com.android.systemui.communal.data.repository.CommunalSettingsRepository
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.util.kotlin.BooleanFlowOperators.not
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+class HubOnboardingInteractor
+@Inject
+constructor(
+ communalSceneInteractor: CommunalSceneInteractor,
+ communalSettingsRepository: CommunalSettingsRepository,
+ private val communalPrefsInteractor: CommunalPrefsInteractor,
+) {
+ /** Dismiss hub onboarding education. */
+ fun setHubOnboardingDismissed() = communalPrefsInteractor.setHubOnboardingDismissed()
+
+ /** Should hub onboarding be shown to the user. */
+ val shouldShowHubOnboarding: Flow<Boolean> =
+ if (communalSettingsRepository.getV2FlagEnabled()) {
+ allOf(
+ not(communalPrefsInteractor.isHubOnboardingDismissed),
+ communalSceneInteractor.isIdleOnCommunal,
+ )
+ } else {
+ flowOf(false)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/dagger/NoopPosturingModule.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/dagger/NoopPosturingModule.kt
new file mode 100644
index 000000000000..f576a2000e05
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/dagger/NoopPosturingModule.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.posturing.dagger
+
+import com.android.systemui.communal.posturing.data.repository.NoOpPosturingRepository
+import com.android.systemui.communal.posturing.data.repository.PosturingRepository
+import dagger.Binds
+import dagger.Module
+
+/** Module providing a reference implementation of the posturing signal. */
+@Module
+interface NoopPosturingModule {
+ /** Binds a reference implementation of the posturing repository */
+ @Binds fun bindPosturingRepository(impl: NoOpPosturingRepository): PosturingRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt
new file mode 100644
index 000000000000..c5f357f556ca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.posturing.data.repository
+
+import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+@SysUISingleton
+class NoOpPosturingRepository @Inject constructor() : PosturingRepository {
+ override val posturedState: Flow<PosturedState> =
+ MutableStateFlow(PosturedState.Unknown).asStateFlow()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt
new file mode 100644
index 000000000000..dae1a47f5be0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.posturing.data.repository
+
+import com.android.systemui.communal.posturing.shared.model.PosturedState
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Repository which retrieves the postured state of the device. Posturing is defined as the device
+ * being stationary and upright.
+ */
+interface PosturingRepository {
+ /** Whether the device is currently stationary and upright. */
+ val posturedState: Flow<PosturedState>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt
new file mode 100644
index 000000000000..cd81dea9cad1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.posturing.domain.interactor
+
+import com.android.systemui.communal.posturing.data.repository.PosturingRepository
+import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+
+@SysUISingleton
+class PosturingInteractor @Inject constructor(repository: PosturingRepository) {
+ private val debugPostured = MutableStateFlow<PosturedState>(PosturedState.Unknown)
+
+ val postured: Flow<Boolean> =
+ combine(repository.posturedState, debugPostured) { postured, debugValue ->
+ debugValue.asBoolean() ?: postured.asBoolean() ?: false
+ }
+
+ fun setValueForDebug(value: PosturedState) {
+ debugPostured.value = value
+ }
+}
+
+fun PosturedState.asBoolean(): Boolean? {
+ return when (this) {
+ is PosturedState.Postured -> true
+ PosturedState.NotPostured -> false
+ PosturedState.Unknown -> null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt
new file mode 100644
index 000000000000..431ca67315eb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.posturing.shared.model
+
+sealed interface PosturedState {
+ /** Represents postured state */
+ data class Postured(val confidence: Float) : PosturedState
+
+ /** Represents unknown/uninitialized state */
+ data object Unknown : PosturedState
+
+ /** Represents state where we are not postured */
+ data object NotPostured : PosturedState
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/HubOnboardingViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/HubOnboardingViewModel.kt
new file mode 100644
index 000000000000..5245800ba3d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/HubOnboardingViewModel.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.ui.viewmodel
+
+import android.annotation.SuppressLint
+import com.android.systemui.communal.domain.interactor.HubOnboardingInteractor
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+
+class HubOnboardingViewModel
+@AssistedInject
+constructor(private val hubOnboardingInteractor: HubOnboardingInteractor) : ExclusiveActivatable() {
+
+ val shouldShowHubOnboarding = hubOnboardingInteractor.shouldShowHubOnboarding
+
+ fun onDismissed() {
+ hubOnboardingInteractor.setHubOnboardingDismissed()
+ }
+
+ @SuppressLint("MissingPermission")
+ override suspend fun onActivated(): Nothing = coroutineScope { awaitCancellation() }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): HubOnboardingViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 1125d2ccdd97..8e0beda9eff7 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -72,6 +72,7 @@ import android.hardware.display.DisplayManager;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.input.InputManager;
+import android.hardware.location.ContextHubManager;
import android.location.LocationManager;
import android.media.AudioManager;
import android.media.IAudioService;
@@ -238,6 +239,13 @@ public class FrameworkServicesModule {
@Provides
@Singleton
+ @Nullable
+ static ContextHubManager provideContextHubManager(Context context) {
+ return context.getSystemService(ContextHubManager.class);
+ }
+
+ @Provides
+ @Singleton
static DevicePolicyManager provideDevicePolicyManager(Context context) {
return context.getSystemService(DevicePolicyManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 3050cba12f09..3c68e3a09f02 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -30,6 +30,7 @@ import com.android.systemui.accessibility.SystemActionsModule;
import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule;
import com.android.systemui.battery.BatterySaverModule;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayOverrideModule;
+import com.android.systemui.communal.posturing.dagger.NoopPosturingModule;
import com.android.systemui.display.ui.viewmodel.ConnectingDisplayViewModel;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
@@ -74,9 +75,9 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule;
import com.android.systemui.statusbar.notification.dagger.ReferenceNotificationsModule;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpModule;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpModule;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentStartableModule;
@@ -149,6 +150,7 @@ import javax.inject.Named;
RearDisplayModule.class,
RecentsModule.class,
ReferenceNotificationsModule.class,
+ NoopPosturingModule.class,
ReferenceScreenshotModule.class,
RotationLockModule.class,
RotationLockNewModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/grid/ui/compose/SpannedGrids.kt b/packages/SystemUI/src/com/android/systemui/grid/ui/compose/SpannedGrids.kt
index 62ab18bbb738..96ef03c996a9 100644
--- a/packages/SystemUI/src/com/android/systemui/grid/ui/compose/SpannedGrids.kt
+++ b/packages/SystemUI/src/com/android/systemui/grid/ui/compose/SpannedGrids.kt
@@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.key
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
@@ -63,6 +64,7 @@ fun HorizontalSpannedGrid(
rowSpacing: Dp,
spans: List<Int>,
modifier: Modifier = Modifier,
+ keys: (spanIndex: Int) -> Any = { it },
composables: @Composable BoxScope.(spanIndex: Int) -> Unit,
) {
SpannedGrid(
@@ -72,6 +74,7 @@ fun HorizontalSpannedGrid(
spans = spans,
isVertical = false,
modifier = modifier,
+ keys = keys,
composables = composables,
)
}
@@ -103,6 +106,7 @@ fun VerticalSpannedGrid(
rowSpacing: Dp,
spans: List<Int>,
modifier: Modifier = Modifier,
+ keys: (spanIndex: Int) -> Any = { it },
composables: @Composable BoxScope.(spanIndex: Int) -> Unit,
) {
SpannedGrid(
@@ -112,6 +116,7 @@ fun VerticalSpannedGrid(
spans = spans,
isVertical = true,
modifier = modifier,
+ keys = keys,
composables = composables,
)
}
@@ -124,6 +129,7 @@ private fun SpannedGrid(
spans: List<Int>,
isVertical: Boolean,
modifier: Modifier = Modifier,
+ keys: (spanIndex: Int) -> Any = { it },
composables: @Composable BoxScope.(spanIndex: Int) -> Unit,
) {
val crossAxisArrangement = Arrangement.spacedBy(crossAxisSpacing)
@@ -167,17 +173,19 @@ private fun SpannedGrid(
Layout(
{
(0 until spans.size).map { spanIndex ->
- Box(
- Modifier.semantics {
- collectionItemInfo =
- if (isVertical) {
- CollectionItemInfo(spanIndex, 1, 0, 1)
- } else {
- CollectionItemInfo(0, 1, spanIndex, 1)
- }
+ key(keys(spanIndex)) {
+ Box(
+ Modifier.semantics {
+ collectionItemInfo =
+ if (isVertical) {
+ CollectionItemInfo(spanIndex, 1, 0, 1)
+ } else {
+ CollectionItemInfo(0, 1, spanIndex, 1)
+ }
+ }
+ ) {
+ composables(spanIndex)
}
- ) {
- composables(spanIndex)
}
}
},
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
index 84c4bdf1621a..49625f8f9360 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.haptics.msdl.qs
import android.service.quicksettings.Tile
+import androidx.compose.runtime.Stable
import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
@@ -175,6 +176,7 @@ constructor(
}
@SysUISingleton
+@Stable
class TileHapticsViewModelFactoryProvider
@Inject
constructor(private val tileHapticsViewModelFactory: TileHapticsViewModel.Factory) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
index f1945e657d52..bd3d46d09f5e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
@@ -108,22 +108,33 @@ constructor(
private fun setDialogProperties(dialog: SystemUIDialog, uiState: ShortcutCustomizationUiState) {
dialog.setOnDismissListener { viewModel.onDialogDismissed() }
- dialog.setTitle(
- resources.getString(
- when (uiState) {
- is AddShortcutDialog ->
- R.string.shortcut_customize_mode_add_shortcut_description
- is DeleteShortcutDialog ->
- R.string.shortcut_customize_mode_remove_shortcut_description
- else -> R.string.shortcut_customize_mode_reset_shortcut_description
- }
- )
- )
+ dialog.setTitle("${getDialogTitle(uiState)}. ${getDialogDescription(uiState)}")
// By default, apps cannot intercept action key. The system always handles it. This
// flag is needed to enable customisation dialog window to intercept action key
dialog.window?.addPrivateFlags(PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS)
}
+ private fun getDialogTitle(uiState: ShortcutCustomizationUiState): String {
+ return when (uiState) {
+ is AddShortcutDialog -> uiState.shortcutLabel
+ is DeleteShortcutDialog ->
+ resources.getString(R.string.shortcut_customize_mode_remove_shortcut_dialog_title)
+ else ->
+ resources.getString(R.string.shortcut_customize_mode_reset_shortcut_dialog_title)
+ }
+ }
+
+ private fun getDialogDescription(uiState: ShortcutCustomizationUiState): String {
+ return resources.getString(
+ when (uiState) {
+ is AddShortcutDialog -> R.string.shortcut_customize_mode_add_shortcut_description
+ is DeleteShortcutDialog ->
+ R.string.shortcut_customize_mode_remove_shortcut_description
+ else -> R.string.shortcut_customize_mode_reset_shortcut_description
+ }
+ )
+ }
+
@AssistedFactory
interface Factory {
fun create(): ShortcutCustomizationDialogStarter
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
index d8e2dabde8a9..550438aa220f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
@@ -107,16 +107,13 @@ private fun AddShortcutDialog(
onCancel: () -> Unit,
onConfirmSetShortcut: () -> Unit,
) {
- Column(modifier = modifier) {
+ Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
Title(uiState.shortcutLabel)
Description(
text = stringResource(id = R.string.shortcut_customize_mode_add_shortcut_description)
)
PromptShortcutModifier(
- modifier =
- Modifier.padding(top = 24.dp, start = 116.5.dp, end = 116.5.dp)
- .width(131.dp)
- .height(48.dp),
+ modifier = Modifier.padding(top = 24.dp).sizeIn(minWidth = 131.dp, minHeight = 48.dp),
defaultModifierKey = uiState.defaultCustomShortcutModifierKey,
)
SelectedKeyCombinationContainer(
@@ -216,14 +213,14 @@ private fun DialogButtons(
modifier = Modifier.heightIn(40.dp),
contentColor = MaterialTheme.colorScheme.primary,
text = stringResource(R.string.shortcut_helper_customize_dialog_cancel_button_label),
- border = BorderStroke(width = 1.dp, color = MaterialTheme.colorScheme.outlineVariant)
+ border = BorderStroke(width = 1.dp, color = MaterialTheme.colorScheme.outlineVariant),
)
Spacer(modifier = Modifier.width(8.dp))
ShortcutHelperButton(
- modifier = Modifier
- .heightIn(40.dp)
- .focusRequester(focusRequester)
- .focusProperties { canFocus = true }, // enable focus on touch/click mode
+ modifier =
+ Modifier.heightIn(40.dp).focusRequester(focusRequester).focusProperties {
+ canFocus = true
+ }, // enable focus on touch/click mode
onClick = onConfirm,
color = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary,
@@ -236,7 +233,10 @@ private fun DialogButtons(
@Composable
private fun ErrorMessageContainer(errorMessage: String) {
if (errorMessage.isNotEmpty()) {
- Box(modifier = Modifier.padding(horizontal = 16.dp).width(332.dp).height(40.dp)) {
+ Box(
+ modifier =
+ Modifier.padding(horizontal = 16.dp).sizeIn(minWidth = 332.dp, minHeight = 40.dp)
+ ) {
Text(
text = errorMessage,
style = MaterialTheme.typography.bodyMedium,
@@ -405,7 +405,11 @@ private fun PromptShortcutModifier(
modifier: Modifier,
defaultModifierKey: ShortcutKey.Icon.ResIdIcon,
) {
- Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(2.dp)) {
+ Row(
+ modifier = modifier,
+ horizontalArrangement = Arrangement.spacedBy(2.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
ActionKeyContainer(defaultModifierKey)
PlusIconContainer()
}
@@ -422,6 +426,7 @@ private fun ActionKeyContainer(defaultModifierKey: ShortcutKey.Icon.ResIdIcon) {
)
.padding(all = 12.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically,
) {
ActionKeyIcon(defaultModifierKey)
ActionKeyText()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
index a45204d41718..80675d373b8e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
@@ -28,7 +28,6 @@ object BuiltInKeyguardQuickAffordanceKeys {
const val CREATE_NOTE = "create_note"
const val DO_NOT_DISTURB = "do_not_disturb"
const val FLASHLIGHT = "flashlight"
- const val GLANCEABLE_HUB = "glanceable_hub"
const val HOME_CONTROLS = "home"
const val MUTE = "mute"
const val QR_CODE_SCANNER = "qr_code_scanner"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
deleted file mode 100644
index 96b07cc84705..000000000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.keyguard.data.quickaffordance
-
-import android.content.Context
-import android.content.Intent
-import android.provider.Settings
-import android.util.Log
-import com.android.systemui.animation.Expandable
-import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.communal.data.repository.CommunalSceneRepository
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
-import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.communal.shared.model.CommunalTransitionKeys
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.res.R
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.shared.model.Scenes
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
-
-/** Lockscreen affordance that opens the glanceable hub. */
-@SysUISingleton
-class GlanceableHubQuickAffordanceConfig
-@Inject
-constructor(
- @Application private val context: Context,
- private val communalSceneRepository: CommunalSceneRepository,
- private val communalInteractor: CommunalInteractor,
- private val communalSettingsInteractor: CommunalSettingsInteractor,
- private val sceneInteractor: SceneInteractor,
-) : KeyguardQuickAffordanceConfig {
-
- private val pickerNameResourceId = R.string.glanceable_hub_lockscreen_affordance_label
-
- override val key: String = BuiltInKeyguardQuickAffordanceKeys.GLANCEABLE_HUB
-
- override fun pickerName(): String = context.getString(pickerNameResourceId)
-
- override val pickerIconResourceId: Int
- get() = R.drawable.ic_widgets
-
- override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
- get() =
- communalInteractor.isCommunalAvailable.map { available ->
- if (!communalSettingsInteractor.isV2FlagEnabled()) {
- Log.i(TAG, "Button hidden on lockscreen: flag not enabled.")
- KeyguardQuickAffordanceConfig.LockScreenState.Hidden
- } else if (!available) {
- Log.i(TAG, "Button hidden on lockscreen: hub not available.")
- KeyguardQuickAffordanceConfig.LockScreenState.Hidden
- } else {
- KeyguardQuickAffordanceConfig.LockScreenState.Visible(
- icon =
- Icon.Resource(
- pickerIconResourceId,
- ContentDescription.Resource(pickerNameResourceId),
- )
- )
- }
- }
-
- override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
- return if (!communalSettingsInteractor.isV2FlagEnabled()) {
- Log.i(TAG, "Button unavailable in picker: flag not enabled.")
- KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
- } else if (!communalInteractor.isCommunalEnabled.value) {
- Log.i(TAG, "Button disabled in picker: hub not enabled in settings.")
- KeyguardQuickAffordanceConfig.PickerScreenState.Disabled(
- explanation =
- context.getString(R.string.glanceable_hub_lockscreen_affordance_disabled_text),
- actionText =
- context.getString(
- R.string.glanceable_hub_lockscreen_affordance_action_button_label
- ),
- actionIntent = Intent(Settings.ACTION_LOCKSCREEN_SETTINGS),
- )
- } else {
- KeyguardQuickAffordanceConfig.PickerScreenState.Default()
- }
- }
-
- override fun onTriggered(
- expandable: Expandable?
- ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
- if (SceneContainerFlag.isEnabled) {
- sceneInteractor.changeScene(Scenes.Communal, "lockscreen to communal from shortcut")
- } else {
- communalSceneRepository.changeScene(
- CommunalScenes.Communal,
- transitionKey = CommunalTransitionKeys.SimpleFade,
- )
- }
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true)
- }
-
- companion object {
- private const val TAG = "GlanceableHubQuickAffordanceConfig"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
index 8c6fdb989daf..787a9837b860 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
@@ -36,7 +36,6 @@ interface KeyguardDataQuickAffordanceModule {
camera: CameraQuickAffordanceConfig,
doNotDisturb: DoNotDisturbQuickAffordanceConfig,
flashlight: FlashlightQuickAffordanceConfig,
- glanceableHub: GlanceableHubQuickAffordanceConfig,
home: HomeControlsKeyguardQuickAffordanceConfig,
mute: MuteQuickAffordanceConfig,
quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
@@ -47,7 +46,6 @@ interface KeyguardDataQuickAffordanceModule {
camera,
doNotDisturb,
flashlight,
- glanceableHub,
home,
mute,
quickAccessWallet,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index 92b49ed6156c..21c9b0b82b2d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -23,6 +23,7 @@ import android.widget.TextView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.Flags
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
@@ -73,7 +74,6 @@ object KeyguardIndicationAreaBinder {
disposables +=
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
-
launch("$TAG#viewModel.indicationAreaTranslationX") {
viewModel.indicationAreaTranslationX.collect { translationX ->
view.translationX = translationX
@@ -119,6 +119,9 @@ object KeyguardIndicationAreaBinder {
launch("$TAG#viewModel.configurationChange") {
viewModel.configurationChange.collect {
configurationBasedDimensions.value = loadFromResources(view)
+ if (Flags.indicationTextA11yFix()) {
+ indicationController.onConfigurationChanged()
+ }
}
}
@@ -140,7 +143,7 @@ object KeyguardIndicationAreaBinder {
view.resources.getDimensionPixelOffset(R.dimen.keyguard_indication_area_padding),
indicationTextSizePx =
view.resources.getDimensionPixelSize(
- com.android.internal.R.dimen.text_size_small_material,
+ com.android.internal.R.dimen.text_size_small_material
),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index a595d815e016..29bda7623675 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -86,15 +86,56 @@ class ClockSizeTransition(
transition.values[SMARTSPACE_BOUNDS] = targetSSView.getRect()
}
- open fun mutateBounds(
- view: View,
- fromIsVis: Boolean,
- toIsVis: Boolean,
- fromBounds: Rect,
- toBounds: Rect,
- fromSSBounds: Rect?,
- toSSBounds: Rect?,
- ) {}
+ open fun initTargets(from: Target, to: Target) {}
+
+ open fun mutateTargets(from: Target, to: Target) {}
+
+ data class Target(
+ var view: View,
+ var visibility: Int,
+ var isVisible: Boolean,
+ var alpha: Float,
+ var bounds: Rect,
+ var ssBounds: Rect?,
+ ) {
+ companion object {
+ fun fromStart(startValues: TransitionValues): Target {
+ var fromVis = startValues.values[PROP_VISIBILITY] as Int
+ var fromIsVis = fromVis == View.VISIBLE
+ var fromAlpha = startValues.values[PROP_ALPHA] as Float
+
+ // Align starting visibility and alpha
+ if (!fromIsVis) fromAlpha = 0f
+ else if (fromAlpha <= 0f) {
+ fromIsVis = false
+ fromVis = View.INVISIBLE
+ }
+
+ return Target(
+ view = startValues.view,
+ visibility = fromVis,
+ isVisible = fromIsVis,
+ alpha = fromAlpha,
+ bounds = startValues.values[PROP_BOUNDS] as Rect,
+ ssBounds = startValues.values[SMARTSPACE_BOUNDS] as Rect?,
+ )
+ }
+
+ fun fromEnd(endValues: TransitionValues): Target {
+ val toVis = endValues.values[PROP_VISIBILITY] as Int
+ val toIsVis = toVis == View.VISIBLE
+
+ return Target(
+ view = endValues.view,
+ visibility = toVis,
+ isVisible = toIsVis,
+ alpha = if (toIsVis) 1f else 0f,
+ bounds = endValues.values[PROP_BOUNDS] as Rect,
+ ssBounds = endValues.values[SMARTSPACE_BOUNDS] as Rect?,
+ )
+ }
+ }
+ }
override fun createAnimator(
sceenRoot: ViewGroup,
@@ -109,72 +150,58 @@ class ClockSizeTransition(
return null
}
- var fromVis = startValues.values[PROP_VISIBILITY] as Int
- var fromIsVis = fromVis == View.VISIBLE
- var fromAlpha = startValues.values[PROP_ALPHA] as Float
- val fromBounds = startValues.values[PROP_BOUNDS] as Rect
- val fromSSBounds = startValues.values[SMARTSPACE_BOUNDS] as Rect?
-
- val toView = endValues.view
- val toVis = endValues.values[PROP_VISIBILITY] as Int
- val toBounds = endValues.values[PROP_BOUNDS] as Rect
- val toSSBounds = endValues.values[SMARTSPACE_BOUNDS] as Rect?
- val toIsVis = toVis == View.VISIBLE
- val toAlpha = if (toIsVis) 1f else 0f
-
- // Align starting visibility and alpha
- if (!fromIsVis) fromAlpha = 0f
- else if (fromAlpha <= 0f) {
- fromIsVis = false
- fromVis = View.INVISIBLE
- }
+ val from = Target.fromStart(startValues)
+ val to = Target.fromEnd(endValues)
+ initTargets(from, to)
+ mutateTargets(from, to)
- mutateBounds(toView, fromIsVis, toIsVis, fromBounds, toBounds, fromSSBounds, toSSBounds)
- if (fromIsVis == toIsVis && fromBounds.equals(toBounds)) {
+ if (from.isVisible == to.isVisible && from.bounds.equals(to.bounds)) {
if (DEBUG) {
Log.w(
TAG,
- "Skipping no-op transition: $toView; " +
- "vis: $fromVis -> $toVis; " +
- "alpha: $fromAlpha -> $toAlpha; " +
- "bounds: $fromBounds -> $toBounds; ",
+ "Skipping no-op transition: ${to.view}; " +
+ "vis: ${from.visibility} -> ${to.visibility}; " +
+ "alpha: ${from.alpha} -> ${to.alpha}; " +
+ "bounds: ${from.bounds} -> ${to.bounds}; ",
)
}
return null
}
- val sendToBack = fromIsVis && !toIsVis
+ val sendToBack = from.isVisible && !to.isVisible
fun lerp(start: Int, end: Int, fract: Float): Int =
MathUtils.lerp(start.toFloat(), end.toFloat(), fract).toInt()
fun computeBounds(fract: Float): Rect =
Rect(
- lerp(fromBounds.left, toBounds.left, fract),
- lerp(fromBounds.top, toBounds.top, fract),
- lerp(fromBounds.right, toBounds.right, fract),
- lerp(fromBounds.bottom, toBounds.bottom, fract),
+ lerp(from.bounds.left, to.bounds.left, fract),
+ lerp(from.bounds.top, to.bounds.top, fract),
+ lerp(from.bounds.right, to.bounds.right, fract),
+ lerp(from.bounds.bottom, to.bounds.bottom, fract),
)
fun assignAnimValues(src: String, fract: Float, vis: Int? = null) {
+ mutateTargets(from, to)
val bounds = computeBounds(fract)
- val alpha = MathUtils.lerp(fromAlpha, toAlpha, fract)
+ val alpha = MathUtils.lerp(from.alpha, to.alpha, fract)
if (DEBUG) {
Log.i(
TAG,
- "$src: $toView; fract=$fract; alpha=$alpha; vis=$vis; bounds=$bounds;",
+ "$src: ${to.view}; fract=$fract; alpha=$alpha; vis=$vis; bounds=$bounds;",
)
}
- toView.setVisibility(vis ?: View.VISIBLE)
- toView.setAlpha(alpha)
- toView.setRect(bounds)
+
+ to.view.setVisibility(vis ?: View.VISIBLE)
+ to.view.setAlpha(alpha)
+ to.view.setRect(bounds)
}
if (DEBUG) {
Log.i(
TAG,
- "transitioning: $toView; " +
- "vis: $fromVis -> $toVis; " +
- "alpha: $fromAlpha -> $toAlpha; " +
- "bounds: $fromBounds -> $toBounds; ",
+ "transitioning: ${to.view}; " +
+ "vis: ${from.visibility} -> ${to.visibility}; " +
+ "alpha: ${from.alpha} -> ${to.alpha}; " +
+ "bounds: ${from.bounds} -> ${to.bounds}; ",
)
}
@@ -190,11 +217,11 @@ class ClockSizeTransition(
this@VisibilityBoundsTransition.addListener(
object : TransitionListenerAdapter() {
override fun onTransitionStart(t: Transition) {
- toView.viewTreeObserver.addOnPreDrawListener(predrawCallback)
+ to.view.viewTreeObserver.addOnPreDrawListener(predrawCallback)
}
override fun onTransitionEnd(t: Transition) {
- toView.viewTreeObserver.removeOnPreDrawListener(predrawCallback)
+ to.view.viewTreeObserver.removeOnPreDrawListener(predrawCallback)
}
}
)
@@ -202,17 +229,17 @@ class ClockSizeTransition(
val listener =
object : AnimatorListenerAdapter() {
override fun onAnimationStart(anim: Animator) {
- assignAnimValues("start", 0f, fromVis)
+ assignAnimValues("start", 0f, from.visibility)
}
override fun onAnimationEnd(anim: Animator) {
- assignAnimValues("end", 1f, toVis)
- if (sendToBack) toView.translationZ = 0f
+ assignAnimValues("end", 1f, to.visibility)
+ if (sendToBack) to.view.translationZ = 0f
}
}
anim.addListener(listener)
- assignAnimValues("init", 0f, fromVis)
+ assignAnimValues("init", 0f, from.visibility)
}
}
@@ -251,31 +278,23 @@ class ClockSizeTransition(
}
}
- override fun mutateBounds(
- view: View,
- fromIsVis: Boolean,
- toIsVis: Boolean,
- fromBounds: Rect,
- toBounds: Rect,
- fromSSBounds: Rect?,
- toSSBounds: Rect?,
- ) {
+ override fun initTargets(from: Target, to: Target) {
// Move normally if clock is not changing visibility
- if (fromIsVis == toIsVis) return
+ if (from.isVisible == to.isVisible) return
- fromBounds.set(toBounds)
+ from.bounds.set(to.bounds)
if (isLargeClock) {
// Large clock shouldn't move; fromBounds already set
- } else if (toSSBounds != null && fromSSBounds != null) {
+ } else if (to.ssBounds != null && from.ssBounds != null) {
// Instead of moving the small clock the full distance, we compute the distance
// smartspace will move. We then scale this to match the duration of this animation
// so that the small clock moves at the same speed as smartspace.
val ssTranslation =
- abs((toSSBounds.top - fromSSBounds.top) * smallClockMoveScale).toInt()
- fromBounds.top = toBounds.top - ssTranslation
- fromBounds.bottom = toBounds.bottom - ssTranslation
+ abs((to.ssBounds!!.top - from.ssBounds!!.top) * smallClockMoveScale).toInt()
+ from.bounds.top = to.bounds.top - ssTranslation
+ from.bounds.bottom = to.bounds.bottom - ssTranslation
} else {
- Log.e(TAG, "mutateBounds: smallClock received no smartspace bounds")
+ Log.e(TAG, "initTargets: smallClock received no smartspace bounds")
}
}
}
@@ -320,10 +339,9 @@ class ClockSizeTransition(
}
}
- // TODO: Might need a mechanism to update this one while in-progress
class SmartspaceMoveTransition(
val config: IntraBlueprintTransition.Config,
- viewModel: KeyguardClockViewModel,
+ val viewModel: KeyguardClockViewModel,
) : VisibilityBoundsTransition() {
private val isLargeClock = viewModel.isLargeClockVisible.value
override val captureSmartspace = false
@@ -340,23 +358,23 @@ class ClockSizeTransition(
addTarget(R.id.status_view_media_container)
}
- override fun mutateBounds(
- view: View,
- fromIsVis: Boolean,
- toIsVis: Boolean,
- fromBounds: Rect,
- toBounds: Rect,
- fromSSBounds: Rect?,
- toSSBounds: Rect?,
- ) {
+ override fun initTargets(from: Target, to: Target) {
// If view is changing visibility, hold it in place
- if (fromIsVis == toIsVis) return
- if (DEBUG) Log.i(TAG, "Holding position of ${view.id}")
+ if (from.isVisible == to.isVisible) return
+ if (DEBUG) Log.i(TAG, "Holding position of ${to.view.id}")
- if (fromIsVis) {
- toBounds.set(fromBounds)
+ if (from.isVisible) {
+ to.bounds.set(from.bounds)
} else {
- fromBounds.set(toBounds)
+ from.bounds.set(to.bounds)
+ }
+ }
+
+ override fun mutateTargets(from: Target, to: Target) {
+ if (to.view.id == sharedR.id.date_smartspace_view) {
+ to.isVisible = !viewModel.hasCustomWeatherDataDisplay.value
+ to.visibility = if (to.isVisible) View.VISIBLE else View.GONE
+ to.alpha = if (to.isVisible) 1f else 0f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModel.kt
index e68e465ed55a..ba03c48c65e9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModel.kt
@@ -16,10 +16,50 @@
package com.android.systemui.keyguard.ui.viewmodel
+import androidx.compose.runtime.getValue
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
-import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.flowOf
-class KeyguardMediaViewModel @Inject constructor(mediaCarouselInteractor: MediaCarouselInteractor) {
- val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasActiveMediaOrRecommendation
+class KeyguardMediaViewModel
+@AssistedInject
+constructor(
+ mediaCarouselInteractor: MediaCarouselInteractor,
+ keyguardInteractor: KeyguardInteractor,
+) : ExclusiveActivatable() {
+
+ private val hydrator = Hydrator("KeyguardMediaViewModel.hydrator")
+ /**
+ * Whether media carousel is visible on lockscreen. Media may be presented on lockscreen but
+ * still hidden on certain surfaces like AOD
+ */
+ val isMediaVisible: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "isMediaVisible",
+ source =
+ keyguardInteractor.isDozing.flatMapLatestConflated { isDozing ->
+ if (isDozing) {
+ flowOf(false)
+ } else {
+ mediaCarouselInteractor.hasActiveMediaOrRecommendation
+ }
+ },
+ initialValue =
+ !keyguardInteractor.isDozing.value &&
+ mediaCarouselInteractor.hasActiveMediaOrRecommendation.value,
+ )
+
+ override suspend fun onActivated(): Nothing {
+ hydrator.activate()
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): KeyguardMediaViewModel
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 9270fff61c43..05d8bff2ceb6 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -17,7 +17,7 @@
package com.android.systemui.navigationbar;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
@@ -361,7 +361,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mSysUiState.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable)
.setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
.setFlag(SYSUI_STATE_IME_SHOWING,
- (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
.setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
(mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0)
.setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
index c098b1675b87..c78750718cf0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -17,7 +17,7 @@
package com.android.systemui.navigationbar.views;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
@@ -1681,7 +1681,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
.setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
.setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible())
.setFlag(SYSUI_STATE_IME_SHOWING,
- (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
.setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
(mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0)
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
@@ -1938,9 +1938,9 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
final boolean oldBackAlt =
(mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
if (newBackAlt != oldBackAlt) {
- mView.onImeVisibilityChanged(newBackAlt);
- mImeVisible = newBackAlt;
+ mView.onBackAltChanged(newBackAlt);
}
+ mImeVisible = (hints & NAVIGATION_HINT_IME_SHOWN) != 0;
mView.setNavigationIconHints(hints);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
index c4abcd2afc4f..d5ae72165c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
@@ -561,8 +561,15 @@ public class NavigationBarView extends FrameLayout {
updateNavButtonIcons();
}
- void onImeVisibilityChanged(boolean visible) {
- if (!visible) {
+ /**
+ * Called when the boolean value of whether to adjust the back button for the IME changed.
+ *
+ * @param useBackAlt whether to adjust the back button for the IME.
+ *
+ * @see android.inputmethodservice.InputMethodService.BackDispositionMode
+ */
+ void onBackAltChanged(boolean useBackAlt) {
+ if (!useBackAlt) {
mTransitionListener.onBackAltCleared();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
index 4fe63379aed4..8aad61a8c7cb 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
@@ -52,6 +52,13 @@ constructor(
private val hydrator = Hydrator("NotificationsShadeOverlayContentViewModel.hydrator")
+ val isShadeLayoutWide: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "isShadeLayoutWide",
+ initialValue = shadeInteractor.isShadeLayoutWide.value,
+ source = shadeInteractor.isShadeLayoutWide,
+ )
+
val showHeader: Boolean by
hydrator.hydratedStateOf(
traceName = "showHeader",
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt
deleted file mode 100644
index 398ace4b67f4..000000000000
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.notifications.ui.viewmodel
-
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
-import com.android.systemui.scene.shared.model.Overlays
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
-import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-
-/**
- * Models the UI state for the user actions that the user can perform to navigate to other scenes.
- */
-class NotificationsShadeUserActionsViewModel @AssistedInject constructor() :
- UserActionsViewModel() {
-
- override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
- setActions(
- mapOf(
- Back to SceneFamilies.Home,
- Swipe.Up to SceneFamilies.Home,
- Swipe.Down(fromSource = SceneContainerEdge.TopRight) to
- ReplaceByOverlay(Overlays.QuickSettingsShade),
- )
- )
- }
-
- @AssistedFactory
- interface Factory {
- fun create(): NotificationsShadeUserActionsViewModel
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt
index c9d767e6d152..302242ca11dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt
@@ -16,7 +16,7 @@
package com.android.systemui.qs.panels.ui.compose
-import android.processor.immutability.Immutable
+import androidx.compose.runtime.Stable
import com.android.compose.animation.Bounceable
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.ui.model.GridCell
@@ -24,7 +24,7 @@ import com.android.systemui.qs.panels.ui.model.TileGridCell
import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
-@Immutable
+@Stable
data class BounceableInfo(
val bounceable: BounceableTileViewModel,
val previousTile: Bounceable?,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index 5cb30b999e13..b084f79a5bba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -19,8 +19,8 @@ package com.android.systemui.qs.panels.ui.compose
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.key
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
@@ -49,6 +49,8 @@ fun ContentScope.QuickQuickSettings(
val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle()
val scope = rememberCoroutineScope()
+ val spans by remember(sizedTiles) { derivedStateOf { sizedTiles.fastMap { it.width } } }
+
DisposableEffect(tiles) {
val token = Any()
tiles.forEach { it.startListening(token) }
@@ -62,26 +64,24 @@ fun ContentScope.QuickQuickSettings(
columns = columns,
columnSpacing = dimensionResource(R.dimen.qs_tile_margin_horizontal),
rowSpacing = dimensionResource(R.dimen.qs_tile_margin_vertical),
- spans = sizedTiles.fastMap { it.width },
+ spans = spans,
modifier = Modifier.sysuiResTag("qqs_tile_layout"),
+ keys = { sizedTiles[it].tile.spec },
) { spanIndex ->
val it = sizedTiles[spanIndex]
val column = cellIndex % columns
cellIndex += it.width
- key(it.tile.spec) {
- Tile(
- tile = it.tile,
- iconOnly = it.isIcon,
- modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
- squishiness = { squishiness },
- coroutineScope = scope,
- bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
- tileHapticsViewModelFactoryProvider =
- viewModel.tileHapticsViewModelFactoryProvider,
- // There should be no QuickQuickSettings when the details view is enabled.
- detailsViewModel = null,
- )
- }
+ Tile(
+ tile = it.tile,
+ iconOnly = it.isIcon,
+ modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
+ squishiness = { squishiness },
+ coroutineScope = scope,
+ bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
+ tileHapticsViewModelFactoryProvider = viewModel.tileHapticsViewModelFactoryProvider,
+ // There should be no QuickQuickSettings when the details view is enabled.
+ detailsViewModel = null,
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
index 1bfbbe1964dc..30fb50db82a2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
@@ -38,12 +38,11 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.em
import com.android.systemui.qs.flags.QsDetailedView
import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel
@Composable
-fun TileDetails(detailsViewModel: DetailsViewModel) {
+fun TileDetails(modifier: Modifier = Modifier, detailsViewModel: DetailsViewModel) {
if (!QsDetailedView.isEnabled) {
throw IllegalStateException("QsDetailedView should be enabled")
@@ -54,10 +53,11 @@ fun TileDetails(detailsViewModel: DetailsViewModel) {
DisposableEffect(Unit) { onDispose { detailsViewModel.closeDetailedView() } }
Column(
- modifier = Modifier
- .fillMaxWidth()
- // The height of the details view is TBD.
- .fillMaxHeight()
+ modifier =
+ modifier
+ .fillMaxWidth()
+ // The height of the details view is TBD.
+ .fillMaxHeight()
) {
CompositionLocalProvider(
value = LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant
@@ -65,15 +65,14 @@ fun TileDetails(detailsViewModel: DetailsViewModel) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
+ verticalAlignment = Alignment.CenterVertically,
) {
-
IconButton(
onClick = { detailsViewModel.closeDetailedView() },
- modifier = Modifier
- .align(Alignment.CenterVertically)
- .height(TileDetailsDefaults.IconHeight)
- .padding(start = TileDetailsDefaults.IconPadding),
+ modifier =
+ Modifier.align(Alignment.CenterVertically)
+ .height(TileDetailsDefaults.IconHeight)
+ .padding(start = TileDetailsDefaults.IconPadding),
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
@@ -83,17 +82,16 @@ fun TileDetails(detailsViewModel: DetailsViewModel) {
}
Text(
text = tileDetailedViewModel.getTitle(),
- modifier = Modifier
- .align(Alignment.CenterVertically),
+ modifier = Modifier.align(Alignment.CenterVertically),
textAlign = TextAlign.Center,
- style = MaterialTheme.typography.titleLarge
+ style = MaterialTheme.typography.titleLarge,
)
IconButton(
onClick = { tileDetailedViewModel.clickOnSettingsButton() },
- modifier = Modifier
- .align(Alignment.CenterVertically)
- .height(TileDetailsDefaults.IconHeight)
- .padding(end = TileDetailsDefaults.IconPadding),
+ modifier =
+ Modifier.align(Alignment.CenterVertically)
+ .height(TileDetailsDefaults.IconHeight)
+ .padding(end = TileDetailsDefaults.IconPadding),
) {
Icon(
imageVector = Icons.Default.Settings,
@@ -104,11 +102,9 @@ fun TileDetails(detailsViewModel: DetailsViewModel) {
}
Text(
text = tileDetailedViewModel.getSubTitle(),
- modifier = Modifier
- .fillMaxWidth(),
+ modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
- style = MaterialTheme.typography.titleSmall
-
+ style = MaterialTheme.typography.titleSmall,
)
}
tileDetailedViewModel.GetContentView()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
index 4432d336237f..cc4c3af1dc63 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
@@ -18,8 +18,8 @@ package com.android.systemui.qs.panels.ui.compose.infinitegrid
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.key
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
@@ -86,27 +86,28 @@ constructor(
val scope = rememberCoroutineScope()
var cellIndex = 0
+ val spans by remember(sizedTiles) { derivedStateOf { sizedTiles.fastMap { it.width } } }
+
VerticalSpannedGrid(
columns = columns,
columnSpacing = dimensionResource(R.dimen.qs_tile_margin_horizontal),
rowSpacing = dimensionResource(R.dimen.qs_tile_margin_vertical),
- spans = sizedTiles.fastMap { it.width },
+ spans = spans,
+ keys = { sizedTiles[it].tile.spec },
) { spanIndex ->
val it = sizedTiles[spanIndex]
val column = cellIndex % columns
cellIndex += it.width
- key(it.tile.spec) {
- Tile(
- tile = it.tile,
- iconOnly = iconTilesViewModel.isIconTile(it.tile.spec),
- modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
- squishiness = { squishiness },
- tileHapticsViewModelFactoryProvider = tileHapticsViewModelFactoryProvider,
- coroutineScope = scope,
- bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
- detailsViewModel = detailsViewModel,
- )
- }
+ Tile(
+ tile = it.tile,
+ iconOnly = iconTilesViewModel.isIconTile(it.tile.spec),
+ modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
+ squishiness = { squishiness },
+ tileHapticsViewModelFactoryProvider = tileHapticsViewModelFactoryProvider,
+ coroutineScope = scope,
+ bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
+ detailsViewModel = detailsViewModel,
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
index 16c27223a471..8a627c452081 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
@@ -18,6 +18,7 @@ package com.android.systemui.qs.pipeline.shared
import android.content.ComponentName
import android.text.TextUtils
+import androidx.compose.runtime.Stable
import com.android.systemui.qs.external.CustomTile
/**
@@ -34,6 +35,7 @@ sealed class TileSpec private constructor(open val spec: String) {
data object Invalid : TileSpec("")
/** Container for the spec of a tile provided by SystemUI. */
+ @Stable
data class PlatformTileSpec internal constructor(override val spec: String) : TileSpec(spec) {
override fun toString(): String {
return "P($spec)"
@@ -45,6 +47,7 @@ sealed class TileSpec private constructor(open val spec: String) {
*
* [componentName] indicates the associated `TileService`.
*/
+ @Stable
data class CustomTileSpec
internal constructor(override val spec: String, val componentName: ComponentName) :
TileSpec(spec) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
index 91d907952bc6..c7db04a6b7b2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.ui.viewmodel
+import androidx.compose.runtime.getValue
import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel
@@ -23,6 +24,7 @@ import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
import com.android.systemui.qs.panels.ui.viewmodel.toolbar.ToolbarViewModel
+import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -35,6 +37,7 @@ class QuickSettingsContainerViewModel
constructor(
brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory,
quickQuickSettingsViewModelFactory: QuickQuickSettingsViewModel.Factory,
+ shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
@Assisted supportsBrightnessMirroring: Boolean,
val tileGridViewModel: TileGridViewModel,
val editModeViewModel: EditModeViewModel,
@@ -47,10 +50,13 @@ constructor(
val quickQuickSettingsViewModel = quickQuickSettingsViewModelFactory.create()
+ val shadeHeaderViewModel = shadeHeaderViewModelFactory.create()
+
override suspend fun onActivated(): Nothing {
coroutineScope {
launch { brightnessSliderViewModel.activate() }
launch { quickQuickSettingsViewModel.activate() }
+ launch { shadeHeaderViewModel.activate() }
awaitCancellation()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
index a108bc2c4a8c..d9df1ef36847 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
@@ -52,6 +52,13 @@ constructor(
private val hydrator = Hydrator("QuickSettingsContainerViewModel.hydrator")
+ val isShadeLayoutWide: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "isShadeLayoutWide",
+ initialValue = shadeInteractor.isShadeLayoutWide.value,
+ source = shadeInteractor.isShadeLayoutWide,
+ )
+
val showHeader: Boolean by
hydrator.hydratedStateOf(
traceName = "showHeader",
@@ -61,6 +68,13 @@ constructor(
val quickSettingsContainerViewModel = quickSettingsContainerViewModelFactory.create(false)
+ val showQuickSettingsOverlayHeader: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "showQuickSettingsOverlayHeader",
+ initialValue = shadeInteractor.isShadeLayoutWide.value,
+ source = shadeInteractor.isShadeLayoutWide,
+ )
+
override suspend fun onActivated(): Nothing {
coroutineScope {
launch { hydrator.activate() }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index adf9eb44e162..60c2cca1ae8b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -61,7 +61,6 @@ import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.PatternMatcher;
-import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -100,6 +99,7 @@ import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.navigationbar.views.NavigationBar;
import com.android.systemui.navigationbar.views.NavigationBarView;
import com.android.systemui.navigationbar.views.buttons.KeyButtonView;
+import com.android.systemui.process.ProcessWrapper;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
@@ -675,11 +675,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
DumpManager dumpManager,
Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder,
BroadcastDispatcher broadcastDispatcher,
- Optional<BackAnimation> backAnimation
+ Optional<BackAnimation> backAnimation,
+ ProcessWrapper processWrapper
) {
// b/241601880: This component should only be running for primary users or
// secondaryUsers when visibleBackgroundUsers are supported.
- boolean isSystemUser = Process.myUserHandle().equals(UserHandle.SYSTEM);
+ boolean isSystemUser = processWrapper.isSystemUser();
boolean isVisibleBackgroundUser =
userManager.isVisibleBackgroundUsersSupported() && !userManager.isUserForeground();
if (!isSystemUser && isVisibleBackgroundUser) {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
index 2a0a22f32601..6e79d568761f 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
@@ -18,6 +18,7 @@
package com.android.systemui.scene.domain.resolver
+import android.util.Log
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -85,8 +86,8 @@ constructor(
isUnlocked: Boolean,
isDreamingWithOverlay: Boolean,
isAbleToDream: Boolean,
- ): SceneKey =
- when {
+ ): SceneKey {
+ val result = when {
// Dream can run even if Keyguard is disabled, thus it has the highest priority here.
isDreamingWithOverlay && isAbleToDream -> Scenes.Dream
!isKeyguardEnabled -> Scenes.Gone
@@ -95,8 +96,21 @@ constructor(
!isUnlocked -> Scenes.Lockscreen
else -> Scenes.Gone
}
+ Log.d(TAG, "homeScene emitting $result, values:")
+ Log.d(TAG, " isKeyguardEnabled=$isKeyguardEnabled")
+ Log.d(TAG, " canSwipeToEnter=$canSwipeToEnter")
+ Log.d(TAG, " isDeviceEntered=$isDeviceEntered" )
+ Log.d(TAG, " isUnlocked=$isUnlocked")
+ Log.d(TAG, " isDreamingWithOverlay=$isDreamingWithOverlay")
+ Log.d(TAG, " isAbleToDream=$isAbleToDream")
+ Log.d(TAG, "")
+ return result
+ }
companion object {
+
+ private const val TAG = "HomeSceneFamilyResolver"
+
val homeScenes =
setOf(
Scenes.Gone,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 8657c1723507..ecd002705c60 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -564,7 +564,7 @@ constructor(
.collect {
switchToScene(
targetSceneKey = Scenes.Lockscreen,
- loggingReason = "device became non-interactive",
+ loggingReason = "device became non-interactive (SceneContainerStartable)",
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
index 08214c456897..f5c605211520 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
@@ -35,7 +35,6 @@ import android.util.Log
import android.view.Display
import android.view.ScrollCaptureResponse
import android.view.ViewRootImpl.ActivityConfigCallback
-import android.view.WindowManager
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import android.widget.Toast
import android.window.WindowContext
@@ -218,9 +217,7 @@ internal constructor(
window.setFocusable(true)
viewProxy.requestFocus()
- if (screenshot.type != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
- enqueueScrollCaptureRequest(requestId, screenshot.userHandle)
- }
+ enqueueScrollCaptureRequest(requestId, screenshot.userHandle)
window.attachWindow()
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotCrossProfileService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotCrossProfileService.kt
index 2e6c7567259f..d82a8bd0ddcd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotCrossProfileService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotCrossProfileService.kt
@@ -31,7 +31,7 @@ class ScreenshotCrossProfileService : Service() {
private val mBinder: IBinder =
object : ICrossProfileService.Stub() {
- override fun launchIntent(intent: Intent, bundle: Bundle) {
+ override fun launchIntent(intent: Intent, bundle: Bundle?) {
startActivity(intent, bundle)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
index 7a70966c2b12..b15615a83698 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
@@ -5,6 +5,7 @@ import android.view.DisplayCutout
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.res.R
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
import javax.inject.Inject
/**
@@ -15,11 +16,9 @@ class QsBatteryModeController
@Inject
constructor(
@ShadeDisplayAware private val context: Context,
- insetsProviderStore: StatusBarContentInsetsProviderStore,
+ private val insetsProviderStore: StatusBarContentInsetsProviderStore,
) {
- private val insetsProvider = insetsProviderStore.defaultDisplay
-
private companion object {
// MotionLayout frames are in [0, 100]. Where 0 and 100 are reserved for start and end
// frames.
@@ -43,17 +42,19 @@ constructor(
* animation.
*/
@BatteryMeterView.BatteryPercentMode
- fun getBatteryMode(cutout: DisplayCutout?, qsExpandedFraction: Float): Int? =
- when {
+ fun getBatteryMode(cutout: DisplayCutout?, qsExpandedFraction: Float): Int? {
+ val insetsProvider = insetsProviderStore.forDisplay(context.displayId)
+ return when {
qsExpandedFraction > fadeInStartFraction -> BatteryMeterView.MODE_ESTIMATE
- qsExpandedFraction < fadeOutCompleteFraction ->
- if (hasCenterCutout(cutout)) {
+ insetsProvider != null && qsExpandedFraction < fadeOutCompleteFraction ->
+ if (hasCenterCutout(cutout, insetsProvider)) {
BatteryMeterView.MODE_ON
} else {
BatteryMeterView.MODE_ESTIMATE
}
else -> null
}
+ }
fun updateResources() {
fadeInStartFraction =
@@ -64,7 +65,10 @@ constructor(
MOTION_LAYOUT_MAX_FRAME.toFloat()
}
- private fun hasCenterCutout(cutout: DisplayCutout?): Boolean =
+ private fun hasCenterCutout(
+ cutout: DisplayCutout?,
+ insetsProvider: StatusBarContentInsetsProvider,
+ ): Boolean =
cutout?.let {
!insetsProvider.currentRotationHasCornerCutout() && !it.boundingRectTop.isEmpty
} ?: false
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
index 747642097327..f926d39760fe 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
@@ -36,7 +36,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.res.R
import com.android.systemui.scene.ui.view.WindowRootView
-import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository
import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.shade.data.repository.ShadeDisplaysRepositoryImpl
import com.android.systemui.shade.display.ShadeDisplayPolicyModule
@@ -211,15 +210,6 @@ object ShadeDisplayAwareModule {
return impl
}
- @SysUISingleton
- @Provides
- fun provideMutableShadePositionRepository(
- impl: ShadeDisplaysRepositoryImpl
- ): MutableShadeDisplaysRepository {
- ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
- return impl
- }
-
@Provides
@SysUISingleton
fun provideShadeDialogContextInteractor(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayChangeLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayChangeLatencyTracker.kt
index ec9bba7c1f0f..13b540aa54ba 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayChangeLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayChangeLatencyTracker.kt
@@ -16,7 +16,6 @@
package com.android.systemui.shade
import android.util.Log
-import android.view.Display
import com.android.app.tracing.coroutines.TrackTracer
import com.android.internal.util.LatencyTracker
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
@@ -32,11 +31,9 @@ import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeout
@@ -73,12 +70,7 @@ constructor(
/**
* We need to keep this always up to date eagerly to avoid delays receiving the new display ID.
*/
- private val onMovedToDisplayFlow: StateFlow<Int> =
- configurationRepository.onMovedToDisplay.stateIn(
- bgScope,
- SharingStarted.Eagerly,
- Display.DEFAULT_DISPLAY,
- )
+ private val onMovedToDisplayFlow: StateFlow<Int> = configurationRepository.onMovedToDisplay
private var previousJob: Job? = null
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index e8a792c30aa2..fbc608eddc14 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -57,6 +57,7 @@ import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONS
import com.android.systemui.shade.ShadeViewProviderModule.Companion.SHADE_HEADER
import com.android.systemui.shade.carrier.ShadeCarrierGroup
import com.android.systemui.shade.carrier.ShadeCarrierGroupController
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.StatusIconContainer
@@ -90,8 +91,9 @@ constructor(
private val statusBarIconController: StatusBarIconController,
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val privacyIconsController: HeaderPrivacyIconsController,
- private val insetsProviderStore: StatusBarContentInsetsProviderStore,
+ private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
@ShadeDisplayAware private val configurationController: ConfigurationController,
+ private val shadeDisplaysRepository: ShadeDisplaysRepository,
private val variableDateViewControllerFactory: VariableDateViewController.Factory,
@Named(SHADE_HEADER) private val batteryMeterViewController: BatteryMeterViewController,
private val dumpManager: DumpManager,
@@ -104,7 +106,9 @@ constructor(
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
) : ViewController<View>(header), Dumpable {
- private val insetsProvider = insetsProviderStore.defaultDisplay
+ private val statusBarContentInsetsProvider
+ get() =
+ statusBarContentInsetsProviderStore.forDisplay(shadeDisplaysRepository.displayId.value)
companion object {
/** IDs for transitions and constraints for the [MotionLayout]. */
@@ -414,6 +418,7 @@ constructor(
}
private fun updateConstraintsForInsets(view: MotionLayout, insets: WindowInsets) {
+ val insetsProvider = statusBarContentInsetsProvider ?: return
val cutout = insets.displayCutout.also { this.cutout = it }
val sbInsets: Insets = insetsProvider.getStatusBarContentInsetsForCurrentRotation()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
index 7bfe40c3d811..173da336c62f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
@@ -20,7 +20,7 @@ import android.provider.Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.display.data.repository.DisplayRepository
-import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.shade.display.ShadeDisplayPolicy
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -35,7 +35,7 @@ constructor(
private val globalSettings: GlobalSettings,
private val commandRegistry: CommandRegistry,
private val displaysRepository: DisplayRepository,
- private val positionRepository: MutableShadeDisplaysRepository,
+ private val positionRepository: ShadeDisplaysRepository,
private val policies: Set<@JvmSuppressWildcards ShadeDisplayPolicy>,
private val defaultPolicy: ShadeDisplayPolicy,
) : Command, CoreStartable {
@@ -103,7 +103,7 @@ constructor(
}
private fun printPolicies() {
- val currentPolicyName = positionRepository.policy.value.name
+ val currentPolicyName = positionRepository.currentPolicy.name
pw.println("Available policies: ")
policies.forEach {
pw.print(" - ${it.name}")
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt
index 732d4d1500e7..3513334f2a5c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt
@@ -17,6 +17,8 @@
package com.android.systemui.shade.data.repository
import android.view.Display
+import com.android.systemui.shade.display.FakeShadeDisplayPolicy
+import com.android.systemui.shade.display.ShadeDisplayPolicy
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -30,7 +32,6 @@ class FakeShadeDisplayRepository : ShadeDisplaysRepository {
override val displayId: StateFlow<Int>
get() = _displayId
- fun resetDisplayId() {
- _displayId.value = Display.DEFAULT_DISPLAY
- }
+ override val currentPolicy: ShadeDisplayPolicy
+ get() = FakeShadeDisplayPolicy
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
index af48231e0a99..f959f7fe0c31 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
@@ -20,14 +20,18 @@ import android.provider.Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS
import android.view.Display
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.shade.ShadeOnDefaultDisplayWhenLocked
import com.android.systemui.shade.display.ShadeDisplayPolicy
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
@@ -38,12 +42,8 @@ import kotlinx.coroutines.flow.stateIn
interface ShadeDisplaysRepository {
/** ID of the display which currently hosts the shade */
val displayId: StateFlow<Int>
-}
-
-/** Allows to change the policy that determines in which display the Shade window is visible. */
-interface MutableShadeDisplaysRepository : ShadeDisplaysRepository {
- /** Updates the policy to select where the shade is visible. */
- val policy: StateFlow<ShadeDisplayPolicy>
+ /** The current policy set. */
+ val currentPolicy: ShadeDisplayPolicy
}
/** Keeps the policy and propagates the display id for the shade from it. */
@@ -56,9 +56,11 @@ constructor(
defaultPolicy: ShadeDisplayPolicy,
@Background bgScope: CoroutineScope,
policies: Set<@JvmSuppressWildcards ShadeDisplayPolicy>,
-) : MutableShadeDisplaysRepository {
+ @ShadeOnDefaultDisplayWhenLocked private val shadeOnDefaultDisplayWhenLocked: Boolean,
+ keyguardRepository: KeyguardRepository,
+) : ShadeDisplaysRepository {
- override val policy: StateFlow<ShadeDisplayPolicy> =
+ private val policy: StateFlow<ShadeDisplayPolicy> =
globalSettings
.observerFlow(DEVELOPMENT_SHADE_DISPLAY_AWARENESS)
.onStart { emit(Unit) }
@@ -71,10 +73,32 @@ constructor(
return@map defaultPolicy
}
.distinctUntilChanged()
- .stateIn(bgScope, SharingStarted.WhileSubscribed(), defaultPolicy)
+ .stateIn(bgScope, SharingStarted.Eagerly, defaultPolicy)
+
+ private val displayIdFromPolicy: Flow<Int> = policy.flatMapLatest { it.displayId }
+
+ private val keyguardAwareDisplayPolicy: Flow<Int> =
+ if (!shadeOnDefaultDisplayWhenLocked) {
+ displayIdFromPolicy
+ } else {
+ keyguardRepository.isKeyguardShowing.combine(displayIdFromPolicy) {
+ isKeyguardShowing,
+ currentDisplayId ->
+ if (isKeyguardShowing) {
+ Display.DEFAULT_DISPLAY
+ } else {
+ currentDisplayId
+ }
+ }
+ }
+
+ override val currentPolicy: ShadeDisplayPolicy
+ get() = policy.value
override val displayId: StateFlow<Int> =
- policy
- .flatMapLatest { it.displayId }
- .stateIn(bgScope, SharingStarted.WhileSubscribed(), Display.DEFAULT_DISPLAY)
+ keyguardAwareDisplayPolicy.stateIn(
+ bgScope,
+ SharingStarted.WhileSubscribed(),
+ Display.DEFAULT_DISPLAY,
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/FakeShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/FakeShadeDisplayPolicy.kt
new file mode 100644
index 000000000000..e010bd6f9880
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/FakeShadeDisplayPolicy.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.display
+
+import android.view.Display
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+/** Used only for testing. */
+object FakeShadeDisplayPolicy : ShadeDisplayPolicy {
+ override val name: String
+ get() = "fake_shade_policy"
+
+ override val displayId: StateFlow<Int>
+ get() = _displayId
+
+ private val _displayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
+
+ fun setDisplayId(displayId: Int) {
+ _displayId.value = displayId
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/FocusShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/FocusShadeDisplayPolicy.kt
new file mode 100644
index 000000000000..7d8f7c59ad66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/FocusShadeDisplayPolicy.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.display
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.display.data.repository.FocusedDisplayRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+/** Policy that just emits the [FocusedDisplayRepository] display id. */
+@SysUISingleton
+class FocusShadeDisplayPolicy
+@Inject
+constructor(private val focusedDisplayRepository: FocusedDisplayRepository) : ShadeDisplayPolicy {
+ override val name: String
+ get() = "focused_display"
+
+ override val displayId: StateFlow<Int>
+ get() = focusedDisplayRepository.focusedDisplayId
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
index d53f9f7ec595..677e41a47afe 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
@@ -19,7 +19,8 @@ package com.android.systemui.shade.display
import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractor.ShadeElement
import dagger.Binds
import dagger.Module
-import dagger.multibindings.IntoSet
+import dagger.Provides
+import dagger.multibindings.ElementsIntoSet
import kotlinx.coroutines.flow.StateFlow
/** Describes the display the shade should be shown in. */
@@ -53,27 +54,25 @@ interface ShadeExpansionIntent {
fun consumeExpansionIntent(): ShadeElement?
}
-@Module
+@Module(includes = [AllShadeDisplayPoliciesModule::class])
interface ShadeDisplayPolicyModule {
- @Binds fun provideDefaultPolicy(impl: StatusBarTouchShadeDisplayPolicy): ShadeDisplayPolicy
+ @Binds fun provideDefaultPolicy(impl: DefaultDisplayShadePolicy): ShadeDisplayPolicy
@Binds
fun provideShadeExpansionIntent(impl: StatusBarTouchShadeDisplayPolicy): ShadeExpansionIntent
+}
- @IntoSet
- @Binds
- fun provideDefaultDisplayPolicyToSet(impl: DefaultDisplayShadePolicy): ShadeDisplayPolicy
-
- @IntoSet
- @Binds
- fun provideAnyExternalShadeDisplayPolicyToSet(
- impl: AnyExternalShadeDisplayPolicy
- ): ShadeDisplayPolicy
-
- @Binds
- @IntoSet
- fun provideStatusBarTouchShadeDisplayPolicy(
- impl: StatusBarTouchShadeDisplayPolicy
- ): ShadeDisplayPolicy
+@Module
+internal object AllShadeDisplayPoliciesModule {
+ @Provides
+ @ElementsIntoSet
+ fun provideShadeDisplayPolicies(
+ defaultPolicy: DefaultDisplayShadePolicy,
+ externalPolicy: AnyExternalShadeDisplayPolicy,
+ statusBarPolicy: StatusBarTouchShadeDisplayPolicy,
+ focusPolicy: FocusShadeDisplayPolicy,
+ ): Set<ShadeDisplayPolicy> {
+ return setOf(defaultPolicy, externalPolicy, statusBarPolicy, focusPolicy)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
index 91020aa7bdb0..b155ada87efd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
@@ -23,8 +23,6 @@ import com.android.app.tracing.coroutines.launchTraced
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
-import com.android.systemui.shade.ShadeOnDefaultDisplayWhenLocked
import com.android.systemui.shade.domain.interactor.NotificationShadeElement
import com.android.systemui.shade.domain.interactor.QSShadeElement
import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractor.ShadeElement
@@ -38,13 +36,10 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
/**
* Moves the shade on the last display that received a status bar touch.
@@ -57,9 +52,7 @@ class StatusBarTouchShadeDisplayPolicy
@Inject
constructor(
displayRepository: DisplayRepository,
- keyguardRepository: KeyguardRepository,
@Background private val backgroundScope: CoroutineScope,
- @ShadeOnDefaultDisplayWhenLocked private val shadeOnDefaultDisplayWhenLocked: Boolean,
private val shadeInteractor: Lazy<ShadeInteractor>,
private val qsShadeElement: Lazy<QSShadeElement>,
private val notificationElement: Lazy<NotificationShadeElement>,
@@ -72,20 +65,7 @@ constructor(
private var latestIntent = AtomicReference<ShadeElement?>()
private var timeoutJob: Job? = null
- override val displayId: StateFlow<Int> =
- if (shadeOnDefaultDisplayWhenLocked) {
- keyguardRepository.isKeyguardShowing
- .combine(currentDisplayId) { isKeyguardShowing, currentDisplayId ->
- if (isKeyguardShowing) {
- Display.DEFAULT_DISPLAY
- } else {
- currentDisplayId
- }
- }
- .stateIn(backgroundScope, SharingStarted.WhileSubscribed(), currentDisplayId.value)
- } else {
- currentDisplayId
- }
+ override val displayId: StateFlow<Int> = currentDisplayId
private var removalListener: Job? = null
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
index fc26499a27a7..b045db464674 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
@@ -20,24 +20,34 @@ import android.util.Log
import android.window.WindowContext
import androidx.annotation.UiThread
import com.android.app.tracing.coroutines.launchTraced
-import com.android.app.tracing.traceSection
import com.android.systemui.CoreStartable
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.ShadeDisplayChangeLatencyTracker
import com.android.systemui.shade.ShadeTraceLogger.logMoveShadeWindowTo
+import com.android.systemui.shade.ShadeTraceLogger.t
import com.android.systemui.shade.ShadeTraceLogger.traceReparenting
import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.shade.display.ShadeExpansionIntent
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.row.NotificationRebindingTracker
+import com.android.systemui.statusbar.notification.stack.NotificationStackRebindingHider
+import com.android.systemui.util.kotlin.getOrNull
import com.android.window.flags.Flags
import java.util.Optional
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext
+import kotlinx.coroutines.withTimeoutOrNull
/** Handles Shade window display change when [ShadeDisplaysRepository.displayId] changes. */
@SysUISingleton
@@ -46,27 +56,29 @@ class ShadeDisplaysInteractor
constructor(
private val shadePositionRepository: ShadeDisplaysRepository,
@ShadeDisplayAware private val shadeContext: WindowContext,
+ @ShadeDisplayAware private val configurationRepository: ConfigurationRepository,
@Background private val bgScope: CoroutineScope,
@Main private val mainThreadContext: CoroutineContext,
private val shadeDisplayChangeLatencyTracker: ShadeDisplayChangeLatencyTracker,
shadeExpandedInteractor: Optional<ShadeExpandedStateInteractor>,
private val shadeExpansionIntent: ShadeExpansionIntent,
+ private val activeNotificationsInteractor: ActiveNotificationsInteractor,
+ private val notificationRebindingTracker: NotificationRebindingTracker,
+ notificationStackRebindingHider: Optional<NotificationStackRebindingHider>,
) : CoreStartable {
- private val shadeExpandedInteractor =
- shadeExpandedInteractor.orElse(null)
- ?: error(
- """
- ShadeExpandedStateInteractor must be provided for ShadeDisplaysInteractor to work.
- If it is not, it means this is being instantiated in a SystemUI variant that shouldn't.
- """
- .trimIndent()
- )
+ private val shadeExpandedInteractor = requireOptional(shadeExpandedInteractor)
+ private val notificationStackRebindingHider = requireOptional(notificationStackRebindingHider)
+
+ private val hasActiveNotifications: Boolean
+ get() = activeNotificationsInteractor.areAnyNotificationsPresentValue
override fun start() {
ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
bgScope.launchTraced(TAG) {
- shadePositionRepository.displayId.collect { displayId -> moveShadeWindowTo(displayId) }
+ shadePositionRepository.displayId.collectLatest { displayId ->
+ moveShadeWindowTo(displayId)
+ }
}
}
@@ -74,24 +86,22 @@ constructor(
private suspend fun moveShadeWindowTo(destinationId: Int) {
Log.d(TAG, "Trying to move shade window to display with id $destinationId")
logMoveShadeWindowTo(destinationId)
- // Why using the shade context here instead of the view's Display?
- // The context's display is updated before the view one, so it is a better indicator of
- // which display the shade is supposed to be at. The View display is updated after the first
- // rendering with the new config.
- val currentDisplay = shadeContext.display
- if (currentDisplay == null) {
- Log.w(TAG, "Current shade display is null")
- return
- }
- val currentId = currentDisplay.displayId
- if (currentId == destinationId) {
- Log.w(TAG, "Trying to move the shade to a display it was already in")
- return
- }
+ var currentId = -1
try {
+ // Why using the shade context here instead of the view's Display?
+ // The context's display is updated before the view one, so it is a better indicator of
+ // which display the shade is supposed to be at. The View display is updated after the
+ // first
+ // rendering with the new config.
+ val currentDisplay = shadeContext.display ?: error("Current shade display is null")
+ currentId = currentDisplay.displayId
+ if (currentId == destinationId) {
+ error("Trying to move the shade to a display it was already in")
+ }
+
withContext(mainThreadContext) {
traceReparenting {
- collapseAndExpandShadeIfNeeded {
+ collapseAndExpandShadeIfNeeded(destinationId) {
shadeDisplayChangeLatencyTracker.onShadeDisplayChanging(destinationId)
reparentToDisplayId(id = destinationId)
}
@@ -107,16 +117,71 @@ constructor(
}
}
- private suspend fun collapseAndExpandShadeIfNeeded(wrapped: () -> Unit) {
+ private suspend fun collapseAndExpandShadeIfNeeded(newDisplayId: Int, reparent: () -> Unit) {
val previouslyExpandedElement = shadeExpandedInteractor.currentlyExpandedElement.value
previouslyExpandedElement?.collapse(reason = COLLAPSE_EXPAND_REASON)
+ val notificationStackHidden =
+ if (!hasActiveNotifications) {
+ // This covers the case the previous move was cancelled before setting the
+ // visibility back. As there are no notifications, nothing can flicker here, and
+ // showing them all of a sudden is ok.
+ notificationStackRebindingHider.setVisible(visible = true, animated = false)
+ false
+ } else {
+ // Hiding as otherwise there might be flickers as the inflation with new dimensions
+ // happens async and views with the old dimensions are not removed until the
+ // inflation succeeds.
+ notificationStackRebindingHider.setVisible(visible = false, animated = false)
+ true
+ }
- wrapped()
+ reparent()
+ val elementToExpand =
+ shadeExpansionIntent.consumeExpansionIntent() ?: previouslyExpandedElement
// If the user was trying to expand a specific shade element, let's make sure to expand
// that one. Otherwise, we can just re-expand the previous expanded element.
- shadeExpansionIntent.consumeExpansionIntent()?.expand(COLLAPSE_EXPAND_REASON)
- ?: previouslyExpandedElement?.expand(reason = COLLAPSE_EXPAND_REASON)
+ elementToExpand?.expand(COLLAPSE_EXPAND_REASON)
+ if (notificationStackHidden) {
+ if (hasActiveNotifications) {
+ // "onMovedToDisplay" is what synchronously triggers the rebinding of views: we need
+ // to wait for it to be received.
+ waitForOnMovedToDisplayDispatchedToView(newDisplayId)
+ waitForNotificationsRebinding()
+ }
+ notificationStackRebindingHider.setVisible(visible = true, animated = true)
+ }
+ }
+
+ private suspend fun waitForOnMovedToDisplayDispatchedToView(newDisplayId: Int) {
+ withContext(bgScope.coroutineContext) {
+ t.traceAsync({
+ "waitForOnMovedToDisplayDispatchedToView(newDisplayId=$newDisplayId)"
+ }) {
+ withTimeoutOrNull(TIMEOUT) {
+ configurationRepository.onMovedToDisplay.filter { it == newDisplayId }.first()
+ t.instant { "onMovedToDisplay received with $newDisplayId" }
+ }
+ ?: errorLog(
+ "Timed out while waiting for onMovedToDisplay to be dispatched to " +
+ "the shade root view in ShadeDisplaysInteractor"
+ )
+ }
+ }
+ }
+
+ private suspend fun waitForNotificationsRebinding() {
+ // here we don't need to wait for rebinding to appear (e.g. going > 0), as it already
+ // happened synchronously when the new configuration was received by ViewConfigCoordinator.
+ t.traceAsync("waiting for notifications rebinding to finish") {
+ withTimeoutOrNull(TIMEOUT) {
+ notificationRebindingTracker.rebindingInProgressCount.first { it == 0 }
+ } ?: errorLog("Timed out while waiting for inflations to finish")
+ }
+ }
+
+ private fun errorLog(s: String) {
+ Log.e(TAG, s)
}
private fun checkContextDisplayMatchesExpected(destinationId: Int) {
@@ -133,11 +198,33 @@ constructor(
@UiThread
private fun reparentToDisplayId(id: Int) {
- traceSection({ "reparentToDisplayId(id=$id)" }) { shadeContext.reparentToDisplay(id) }
+ t.traceSyncAndAsync({ "reparentToDisplayId(id=$id)" }) {
+ shadeContext.reparentToDisplay(id)
+ }
}
private companion object {
const val TAG = "ShadeDisplaysInteractor"
const val COLLAPSE_EXPAND_REASON = "Shade window move"
+ val TIMEOUT = 1.seconds
+
+ /**
+ * [ShadeDisplaysInteractor] is bound in the SystemUI module for all variants, but needs
+ * some specific dependencies to be bound from each variant (e.g.
+ * [ShadeExpandedStateInteractor] or [NotificationStackRebindingHider]). When those are not
+ * bound, this class is not expected to be instantiated, and trying to instantiate it would
+ * crash.
+ */
+ inline fun <reified T> requireOptional(optional: Optional<T>): T {
+ return optional.getOrNull()
+ ?: error(
+ """
+ ${T::class.java.simpleName} must be provided for ShadeDisplaysInteractor to work.
+ If it is not, it means this is being instantiated in a SystemUI variant that
+ shouldn't.
+ """
+ .trimIndent()
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
index a653ca2f80a9..b5e171043741 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shade.domain.interactor
+import android.util.Log
import com.android.app.tracing.coroutines.flow.flowName
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -38,6 +39,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
/** The non-empty SceneInteractor implementation. */
@@ -98,18 +100,36 @@ constructor(
override val isShadeTouchable: Flow<Boolean> =
combine(
- powerInteractor.isAsleep,
- keyguardTransitionInteractor.isInTransition(Edge.create(to = KeyguardState.AOD)),
- keyguardRepository.dozeTransitionModel.map { it.to == DozeStateModel.DOZE_PULSING },
- ) { isAsleep, goingToSleep, isPulsing ->
- when {
- // If the device is going to sleep, only accept touches if we're still
- // animating
- goingToSleep -> dozeParams.shouldControlScreenOff()
+ powerInteractor.isAsleep
+ .onEach { Log.d(TAG, "isShadeTouchable: upstream isAsleep=$it") },
+ keyguardTransitionInteractor
+ .isInTransition(Edge.create(to = KeyguardState.AOD))
+ .onEach {
+ Log.d(
+ TAG,
+ "isShadeTouchable: upstream isTransitioningToAod=$it",
+ )
+ },
+ keyguardRepository.dozeTransitionModel
+ .map { it.to == DozeStateModel.DOZE_PULSING }
+ .onEach {
+ Log.d(TAG, "isShadeTouchable: upstream isPulsing=$it")
+ },
+ ) { isAsleep, isTransitioningToAod, isPulsing ->
+ val downstream = when {
+ // If the device is transitioning to AOD, only accept touches if
+ // still animating.
+ isTransitioningToAod -> dozeParams.shouldControlScreenOff()
// If the device is asleep, only accept touches if there's a pulse
isAsleep -> isPulsing
else -> true
}
+ Log.d(TAG, "isShadeTouchable emitting $downstream, values:")
+ Log.d(TAG, " isAsleep=$isAsleep")
+ Log.d(TAG, " isTransitioningToAod=$isTransitioningToAod")
+ Log.d(TAG, " isPulsing=$isPulsing")
+ Log.d(TAG, "")
+ downstream
}
override val isExpandToQsEnabled: Flow<Boolean> =
@@ -128,4 +148,8 @@ constructor(
disableFlags.isQuickSettingsEnabled() &&
!isDozing
}
+
+ companion object {
+ private const val TAG = "ShadeInteractor"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
index 765810810bb8..992385c8d80e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -199,11 +199,14 @@ constructor(
)
} else if (transitionKey == Instant) {
// TODO(b/356596436): Define instant transition instead of snapToScene().
- sceneInteractor.snapToScene(toScene = SceneFamilies.Home, loggingReason = loggingReason)
+ sceneInteractor.snapToScene(
+ toScene = SceneFamilies.Home,
+ loggingReason = loggingReason + " (collapseNotificationsShade)",
+ )
} else {
sceneInteractor.changeScene(
toScene = SceneFamilies.Home,
- loggingReason = loggingReason,
+ loggingReason = loggingReason + " (collapseNotificationsShade)",
transitionKey =
transitionKey ?: ToSplitShade.takeIf { shadeModeInteractor.isSplitShade },
)
@@ -230,11 +233,14 @@ constructor(
if (bypassNotificationsShade || isSplitShade) SceneFamilies.Home else Scenes.Shade
if (transitionKey == Instant) {
// TODO(b/356596436): Define instant transition instead of snapToScene().
- sceneInteractor.snapToScene(toScene = targetScene, loggingReason = loggingReason)
+ sceneInteractor.snapToScene(
+ toScene = targetScene,
+ loggingReason = loggingReason + " (collapseQuickSettingsShade)",
+ )
} else {
sceneInteractor.changeScene(
toScene = targetScene,
- loggingReason = loggingReason,
+ loggingReason = loggingReason + " (collapseQuickSettingsShade)",
transitionKey = transitionKey ?: ToSplitShade.takeIf { isSplitShade },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
index edf503d03f3e..59d812403777 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
@@ -16,17 +16,20 @@
package com.android.systemui.shade.domain.interactor
+import android.provider.Settings
import androidx.annotation.FloatRange
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
/**
@@ -76,29 +79,53 @@ interface ShadeModeInteractor {
class ShadeModeInteractorImpl
@Inject
-constructor(@Application applicationScope: CoroutineScope, repository: ShadeRepository) :
- ShadeModeInteractor {
+constructor(
+ @Application applicationScope: CoroutineScope,
+ repository: ShadeRepository,
+ secureSettingsRepository: SecureSettingsRepository,
+) : ShadeModeInteractor {
+
+ private val isDualShadeEnabled: Flow<Boolean> =
+ secureSettingsRepository.boolSetting(
+ Settings.Secure.DUAL_SHADE,
+ defaultValue = DUAL_SHADE_ENABLED_DEFAULT,
+ )
override val isShadeLayoutWide: StateFlow<Boolean> = repository.isShadeLayoutWide
override val shadeMode: StateFlow<ShadeMode> =
- isShadeLayoutWide
- .map(this::determineShadeMode)
+ combine(isDualShadeEnabled, repository.isShadeLayoutWide, ::determineShadeMode)
.stateIn(
applicationScope,
SharingStarted.Eagerly,
- initialValue = determineShadeMode(isShadeLayoutWide.value),
+ initialValue =
+ determineShadeMode(
+ isDualShadeEnabled = DUAL_SHADE_ENABLED_DEFAULT,
+ isShadeLayoutWide = repository.isShadeLayoutWide.value,
+ ),
)
@FloatRange(from = 0.0, to = 1.0) override fun getTopEdgeSplitFraction(): Float = 0.5f
- private fun determineShadeMode(isShadeLayoutWide: Boolean): ShadeMode {
+ private fun determineShadeMode(
+ isDualShadeEnabled: Boolean,
+ isShadeLayoutWide: Boolean,
+ ): ShadeMode {
return when {
- DualShade.isEnabled -> ShadeMode.Dual
+ isDualShadeEnabled ||
+ // TODO(b/388793191): This ensures that the dual_shade aconfig flag can also enable
+ // Dual Shade, to avoid breaking unit tests. Remove this once all references to the
+ // flag are removed.
+ DualShade.isEnabled -> ShadeMode.Dual
isShadeLayoutWide -> ShadeMode.Split
else -> ShadeMode.Single
}
}
+
+ companion object {
+ /* Whether the Dual Shade setting is enabled by default. */
+ private const val DUAL_SHADE_ENABLED_DEFAULT = false
+ }
}
class ShadeModeInteractorEmptyImpl @Inject constructor() : ShadeModeInteractor {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/ShadeColors.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/ShadeColors.kt
new file mode 100644
index 000000000000..8db622566e5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/ShadeColors.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ui
+
+import android.content.res.Resources
+import android.graphics.Color
+import com.android.internal.graphics.ColorUtils
+import com.android.systemui.res.R
+
+object ShadeColors {
+ @JvmStatic
+ fun Resources.shadePanel(): Int {
+ val layerAbove =
+ ColorUtils.setAlphaComponent(getColor(R.color.shade_panel_base), (0.4f * 255).toInt())
+ val layerBelow = ColorUtils.setAlphaComponent(Color.WHITE, (0.1f * 255).toInt())
+ return ColorUtils.compositeColors(layerAbove, layerBelow)
+ }
+
+ @JvmStatic
+ fun Resources.notificationScrim(): Int {
+ return ColorUtils.setAlphaComponent(
+ getColor(R.color.notification_scrim_base),
+ (0.5f * 255).toInt(),
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index 0d847d84c820..96128df1b723 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -23,18 +23,23 @@ import android.icu.text.DateFormat
import android.icu.text.DisplayContext
import android.os.UserHandle
import android.provider.Settings
+import androidx.compose.runtime.getValue
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.res.R
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.PrivacyChipInteractor
import com.android.systemui.shade.domain.interactor.ShadeHeaderClockInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
import dagger.assisted.AssistedFactory
@@ -56,6 +61,7 @@ class ShadeHeaderViewModel
constructor(
@ShadeDisplayAware context: Context,
private val activityStarter: ActivityStarter,
+ private val sceneInteractor: SceneInteractor,
private val shadeInteractor: ShadeInteractor,
private val mobileIconsInteractor: MobileIconsInteractor,
val mobileIconsViewModel: MobileIconsViewModel,
@@ -63,6 +69,35 @@ constructor(
private val clockInteractor: ShadeHeaderClockInteractor,
private val broadcastDispatcher: BroadcastDispatcher,
) : ExclusiveActivatable() {
+ private val hydrator = Hydrator("ShadeHeaderViewModel.hydrator")
+
+ val highlightNotificationIcons: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "highlightNotificationIcons",
+ initialValue = false,
+ source =
+ sceneInteractor.currentOverlays.map { overlays ->
+ Overlays.NotificationsShade in overlays
+ },
+ )
+
+ val highlightQuickSettingsIcons: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "highlightQuickSettingsIcons",
+ initialValue = false,
+ source =
+ sceneInteractor.currentOverlays.map { overlays ->
+ Overlays.QuickSettingsShade in overlays
+ },
+ )
+
+ val isShadeLayoutWide: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "isShadeLayoutWide",
+ initialValue = shadeInteractor.isShadeLayoutWide.value,
+ source = shadeInteractor.isShadeLayoutWide,
+ )
+
/** True if there is exactly one mobile connection. */
val isSingleCarrier: StateFlow<Boolean> = mobileIconsInteractor.isSingleCarrier
@@ -128,6 +163,8 @@ constructor(
.collect { _mobileSubIds.value = it }
}
+ launch { hydrator.activate() }
+
awaitCancellation()
}
}
@@ -143,11 +180,41 @@ constructor(
}
/** Notifies that the system icons container was clicked. */
- fun onSystemIconContainerClicked() {
- shadeInteractor.collapseEitherShade(
- loggingReason = "ShadeHeaderViewModel.onSystemIconContainerClicked",
- transitionKey = SlightlyFasterShadeCollapse,
- )
+ fun onNotificationIconChipClicked() {
+ if (shadeInteractor.shadeMode.value !is ShadeMode.Dual) {
+ return
+ }
+ val loggingReason = "ShadeHeaderViewModel.onNotificationIconChipClicked"
+ val currentOverlays = sceneInteractor.currentOverlays.value
+ if (Overlays.NotificationsShade in currentOverlays) {
+ shadeInteractor.collapseNotificationsShade(
+ loggingReason = loggingReason,
+ transitionKey = SlightlyFasterShadeCollapse,
+ )
+ } else {
+ shadeInteractor.expandNotificationsShade(loggingReason)
+ }
+ }
+
+ /** Notifies that the system icons container was clicked. */
+ fun onSystemIconChipClicked() {
+ val loggingReason = "ShadeHeaderViewModel.onSystemIconChipClicked"
+ if (shadeInteractor.shadeMode.value is ShadeMode.Dual) {
+ val currentOverlays = sceneInteractor.currentOverlays.value
+ if (Overlays.QuickSettingsShade in currentOverlays) {
+ shadeInteractor.collapseQuickSettingsShade(
+ loggingReason = loggingReason,
+ transitionKey = SlightlyFasterShadeCollapse,
+ )
+ } else {
+ shadeInteractor.expandQuickSettingsShade(loggingReason)
+ }
+ } else {
+ shadeInteractor.collapseEitherShade(
+ loggingReason = loggingReason,
+ transitionKey = SlightlyFasterShadeCollapse,
+ )
+ }
}
/** Notifies that the shadeCarrierGroup was clicked. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index a5595edcbb95..4269f60e1c2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -88,6 +88,7 @@ import com.android.keyguard.TrustGrantFlags;
import com.android.keyguard.logging.KeyguardLogger;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.Flags;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory;
@@ -199,7 +200,7 @@ public class KeyguardIndicationController {
private CharSequence mBiometricMessage;
private CharSequence mBiometricMessageFollowUp;
private BiometricSourceType mBiometricMessageSource;
- protected ColorStateList mInitialTextColorState;
+ private ColorStateList mInitialTextColorState;
private boolean mVisible;
private boolean mOrganizationOwnedDevice;
@@ -393,13 +394,27 @@ public class KeyguardIndicationController {
return mIndicationArea;
}
+ /**
+ * Notify controller about configuration changes.
+ */
+ public void onConfigurationChanged() {
+ // Get new text color in case theme has changed
+ if (Flags.indicationTextA11yFix()) {
+ setIndicationColorToThemeColor();
+ }
+ }
+
public void setIndicationArea(ViewGroup indicationArea) {
mIndicationArea = indicationArea;
mTopIndicationView = indicationArea.findViewById(R.id.keyguard_indication_text);
mLockScreenIndicationView = indicationArea.findViewById(
R.id.keyguard_indication_text_bottom);
- mInitialTextColorState = mTopIndicationView != null
- ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
+ if (Flags.indicationTextA11yFix()) {
+ setIndicationColorToThemeColor();
+ } else {
+ setIndicationTextColor(mTopIndicationView != null
+ ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE));
+ }
if (mRotateTextViewController != null) {
mRotateTextViewController.destroy();
}
@@ -436,6 +451,12 @@ public class KeyguardIndicationController {
mIsLogoutEnabledCallback);
}
+ @NonNull
+ private ColorStateList wallpaperTextColor() {
+ return ColorStateList.valueOf(
+ Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor));
+ }
+
/**
* Cleanup
*/
@@ -513,7 +534,7 @@ public class KeyguardIndicationController {
.setMessage(mContext.getResources().getString(
com.android.systemui.res.R.string.dismissible_keyguard_swipe)
)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
/* updateImmediately */ true);
} else {
@@ -533,7 +554,7 @@ public class KeyguardIndicationController {
INDICATION_TYPE_DISCLOSURE,
new KeyguardIndication.Builder()
.setMessage(disclosure)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
/* updateImmediately */ false);
}
@@ -602,7 +623,7 @@ public class KeyguardIndicationController {
INDICATION_TYPE_OWNER_INFO,
new KeyguardIndication.Builder()
.setMessage(finalInfo)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
false);
} else {
@@ -624,7 +645,7 @@ public class KeyguardIndicationController {
INDICATION_TYPE_BATTERY,
new KeyguardIndication.Builder()
.setMessage(powerIndication)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
animate);
} else {
@@ -645,7 +666,7 @@ public class KeyguardIndicationController {
new KeyguardIndication.Builder()
.setMessage(mContext.getResources().getText(
com.android.internal.R.string.lockscreen_storage_locked))
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
false);
} else {
@@ -666,7 +687,7 @@ public class KeyguardIndicationController {
.setMessage(mBiometricMessage)
.setForceAccessibilityLiveRegionAssertive()
.setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
true
);
@@ -680,7 +701,7 @@ public class KeyguardIndicationController {
new KeyguardIndication.Builder()
.setMessage(mBiometricMessageFollowUp)
.setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
true
);
@@ -711,7 +732,7 @@ public class KeyguardIndicationController {
INDICATION_TYPE_TRUST,
new KeyguardIndication.Builder()
.setMessage(trustGrantedIndication)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
true);
hideBiometricMessage();
@@ -722,7 +743,7 @@ public class KeyguardIndicationController {
INDICATION_TYPE_TRUST,
new KeyguardIndication.Builder()
.setMessage(trustManagedIndication)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
false);
} else {
@@ -751,7 +772,7 @@ public class KeyguardIndicationController {
INDICATION_TYPE_PERSISTENT_UNLOCK_MESSAGE,
new KeyguardIndication.Builder()
.setMessage(mPersistentUnlockMessage)
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
true);
} else {
@@ -792,7 +813,7 @@ public class KeyguardIndicationController {
new KeyguardIndication.Builder()
.setMessage(mContext.getString(
R.string.keyguard_indication_after_adaptive_auth_lock))
- .setTextColor(mInitialTextColorState)
+ .setTextColor(getInitialTextColorState())
.build(),
true);
} else {
@@ -1179,7 +1200,8 @@ public class KeyguardIndicationController {
} else {
message = mContext.getString(R.string.keyguard_retry);
}
- mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState,
+ mStatusBarKeyguardViewManager.setKeyguardMessage(message,
+ getInitialTextColorState(),
null);
}
} else {
@@ -1232,7 +1254,7 @@ public class KeyguardIndicationController {
public void dump(PrintWriter pw, String[] args) {
pw.println("KeyguardIndicationController:");
- pw.println(" mInitialTextColorState: " + mInitialTextColorState);
+ pw.println(" mInitialTextColorState: " + getInitialTextColorState());
pw.println(" mPowerPluggedInWired: " + mPowerPluggedInWired);
pw.println(" mPowerPluggedIn: " + mPowerPluggedIn);
pw.println(" mPowerCharged: " + mPowerCharged);
@@ -1253,6 +1275,22 @@ public class KeyguardIndicationController {
mRotateTextViewController.dump(pw, args);
}
+ protected ColorStateList getInitialTextColorState() {
+ return mInitialTextColorState;
+ }
+
+ private void setIndicationColorToThemeColor() {
+ mInitialTextColorState = wallpaperTextColor();
+ }
+
+ /**
+ * @deprecated Use {@link #setIndicationColorToThemeColor}
+ */
+ @Deprecated
+ private void setIndicationTextColor(ColorStateList color) {
+ mInitialTextColorState = color;
+ }
+
protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
@Override
public void onTimeChanged() {
@@ -1358,7 +1396,7 @@ public class KeyguardIndicationController {
mBouncerMessageInteractor.setFaceAcquisitionMessage(helpString);
}
mStatusBarKeyguardViewManager.setKeyguardMessage(helpString,
- mInitialTextColorState, biometricSourceType);
+ getInitialTextColorState(), biometricSourceType);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
if (isCoExFaceAcquisitionMessage && msgId == FACE_ACQUIRED_TOO_DARK) {
showBiometricMessage(
@@ -1655,7 +1693,7 @@ public class KeyguardIndicationController {
private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg,
BiometricSourceType biometricSourceType) {
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState,
+ mStatusBarKeyguardViewManager.setKeyguardMessage(errString, getInitialTextColorState(),
biometricSourceType);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
showBiometricMessage(errString, followUpMsg, biometricSourceType);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
index 6ce3228531d2..a682f9674e2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
@@ -30,6 +30,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -47,6 +48,7 @@ import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.ui.compose.modifiers.neverDecreaseWidth
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.rememberChronometerState
@Composable
fun OngoingActivityChip(model: OngoingActivityChipModel.Shown, modifier: Modifier = Modifier) {
@@ -195,11 +197,12 @@ private fun ChipContent(viewModel: OngoingActivityChipModel.Shown, modifier: Mod
val context = LocalContext.current
when (viewModel) {
is OngoingActivityChipModel.Shown.Timer -> {
- ChronometerText(
- startTimeMillis = viewModel.startTimeMs,
+ val timerState = rememberChronometerState(startTimeMillis = viewModel.startTimeMs)
+ Text(
+ text = timerState.currentTimeText,
style = MaterialTheme.typography.labelLarge,
color = Color(viewModel.colors.text(context)),
- modifier = modifier,
+ modifier = modifier.neverDecreaseWidth(),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt
index 1c14d3349027..62789782d0a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,12 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.ui.compose
+package com.android.systemui.statusbar.chips.ui.viewmodel
import android.os.SystemClock
import android.text.format.DateUtils.formatElapsedTime
-import androidx.compose.material3.LocalTextStyle
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
@@ -26,13 +24,9 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.text.TextStyle
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.statusbar.chips.ui.compose.modifiers.neverDecreaseWidth
import kotlinx.coroutines.delay
/** Platform-optimized interface for getting current time */
@@ -59,7 +53,10 @@ class ChronometerState(private val timeSource: TimeSource, private val startTime
/** Remember and manage the ChronometerState */
@Composable
-fun rememberChronometerState(timeSource: TimeSource, startTimeMillis: Long): ChronometerState {
+fun rememberChronometerState(
+ startTimeMillis: Long,
+ timeSource: TimeSource = remember { TimeSource { SystemClock.elapsedRealtime() } },
+): ChronometerState {
val state =
remember(timeSource, startTimeMillis) { ChronometerState(timeSource, startTimeMillis) }
val lifecycleOwner = LocalLifecycleOwner.current
@@ -69,25 +66,3 @@ fun rememberChronometerState(timeSource: TimeSource, startTimeMillis: Long): Chr
return state
}
-
-/**
- * A composable chronometer that displays elapsed time with constrained width. The width of the text
- * is only allowed to increase. This ensures there is no visual jitter when individual digits in the
- * text change due to the timer ticking.
- */
-@Composable
-fun ChronometerText(
- startTimeMillis: Long,
- modifier: Modifier = Modifier,
- color: Color = Color.Unspecified,
- style: TextStyle = LocalTextStyle.current,
- timeSource: TimeSource = remember { TimeSource { SystemClock.elapsedRealtime() } },
-) {
- val state = rememberChronometerState(timeSource, startTimeMillis)
- Text(
- text = state.currentTimeText,
- style = style,
- color = color,
- modifier = modifier.neverDecreaseWidth(),
- )
-}
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 417e57d2205f..5cc79df9130a 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
@@ -836,6 +836,14 @@ public final class NotificationEntry extends ListEntry {
}
/**
+ * Returns whether the NotificationEntry is promoted ongoing.
+ */
+ @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
+ public boolean isOngoingPromoted() {
+ return mSbn.getNotification().isPromotedOngoing();
+ }
+
+ /**
* Returns whether this row is considered blockable (i.e. it's not a system notif
* or is not in an allowList).
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationStackModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationStackModule.kt
new file mode 100644
index 000000000000..6ceeb6aae7a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationStackModule.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dagger
+
+import com.android.systemui.statusbar.notification.stack.NotificationStackRebindingHider
+import com.android.systemui.statusbar.notification.stack.NotificationStackRebindingHiderImpl
+import dagger.Binds
+import dagger.BindsOptionalOf
+import dagger.Module
+
+/**
+ * This is meant to be bound in SystemUI variants with [NotificationStackScrollLayoutController].
+ */
+@Module
+interface NotificationStackGoogleModule {
+ @Binds
+ fun bindNotificationStackRebindingHider(
+ impl: NotificationStackRebindingHiderImpl
+ ): NotificationStackRebindingHider
+}
+
+/** This is meant to be used by all SystemUI variants, also those without NSSL. */
+@Module
+interface NotificationStackModule {
+ @BindsOptionalOf
+ fun bindOptionalOfNotificationStackRebindingHider(): NotificationStackRebindingHider
+}
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 086c32cbae5d..0346108856a2 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
@@ -117,6 +117,7 @@ import javax.inject.Provider;
NotificationMemoryModule.class,
NotificationStatsLoggerModule.class,
NotificationsLogModule.class,
+ NotificationStackModule.class,
})
public interface NotificationsModule {
@Binds
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 598ff09ba3b0..d986aaebc0f8 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
@@ -21,6 +21,7 @@ import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static com.android.systemui.flags.Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE;
+import static com.android.systemui.Flags.notificationsPinnedHunInShade;
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
import static com.android.systemui.statusbar.policy.RemoteInputView.FOCUS_ANIMATION_MIN_SCALE;
@@ -106,6 +107,7 @@ import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
@@ -163,6 +165,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private boolean mShowSnooze = false;
private boolean mIsFaded;
+ private boolean mIsPromotedOngoing = false;
+
/**
* Listener for when {@link ExpandableNotificationRow} is laid out.
*/
@@ -196,6 +200,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private int mMaxSmallHeightBeforeS;
private int mMaxSmallHeight;
private int mMaxExpandedHeight;
+ private int mMaxExpandedHeightForPromotedOngoing;
private int mNotificationLaunchHeight;
private boolean mMustStayOnScreen;
@@ -252,6 +257,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private NotificationGuts mGuts;
private NotificationEntry mEntry;
private String mAppName;
+ private NotificationRebindingTracker mRebindingTracker;
private FalsingManager mFalsingManager;
/**
@@ -330,6 +336,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private boolean mSaveSpaceOnLockscreen;
/**
+ * It is added for unit testing purpose.
+ * Please do not use it for other purposes.
+ */
+ @VisibleForTesting
+ public void setIgnoreLockscreenConstraints(boolean ignoreLockscreenConstraints) {
+ mIgnoreLockscreenConstraints = ignoreLockscreenConstraints;
+ }
+
+ /**
* True if we use intrinsic height regardless of vertical space available on lockscreen.
*/
private boolean mIgnoreLockscreenConstraints;
@@ -803,6 +818,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
private void updateLimitsForView(NotificationContentView layout) {
+ final int maxExpandedHeight;
+ if (isPromotedOngoing()) {
+ maxExpandedHeight = mMaxExpandedHeightForPromotedOngoing;
+ } else {
+ maxExpandedHeight = mMaxExpandedHeight;
+ }
+
View contractedView = layout.getContractedChild();
boolean customView = contractedView != null
&& contractedView.getId()
@@ -823,7 +845,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
smallHeight = mMaxSmallHeightBeforeS;
}
} else if (isCallLayout) {
- smallHeight = mMaxExpandedHeight;
+ smallHeight = maxExpandedHeight;
} else {
smallHeight = mMaxSmallHeight;
}
@@ -847,7 +869,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (headsUpWrapper != null) {
headsUpHeight = Math.max(headsUpHeight, headsUpWrapper.getMinLayoutHeight());
}
- layout.setHeights(smallHeight, headsUpHeight, mMaxExpandedHeight);
+
+ layout.setHeights(smallHeight, headsUpHeight, maxExpandedHeight);
}
@NonNull
@@ -1257,6 +1280,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (mIsSummaryWithChildren) {
return mChildrenContainer.getIntrinsicHeight();
}
+ if (isPromotedOngoing()) {
+ return getMaxExpandHeight();
+ }
if (mExpandedWhenPinned) {
return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
} else if (android.app.Flags.compactHeadsUpNotification()
@@ -1526,7 +1552,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
// TODO: Move content inflation logic out of this call
RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
params.setNeedsReinflation(true);
- mRowContentBindStage.requestRebind(mEntry, null /* callback */);
+
+ var rebindEndCallback = mRebindingTracker.trackRebinding(mEntry.getKey());
+ mRowContentBindStage.requestRebind(mEntry, (e) -> rebindEndCallback.onFinished());
Trace.endSection();
}
@@ -2015,9 +2043,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
SmartReplyConstants smartReplyConstants,
SmartReplyController smartReplyController,
IStatusBarService statusBarService,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ NotificationRebindingTracker notificationRebindingTracker) {
mEntry = entry;
mAppName = appName;
+ mRebindingTracker = notificationRebindingTracker;
if (mMenuRow == null) {
mMenuRow = new NotificationMenuRow(mContext, peopleNotificationIdentifier);
}
@@ -2072,6 +2102,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
mMaxExpandedHeight = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_height);
+ mMaxExpandedHeightForPromotedOngoing = NotificationUtils.getFontScaledHeight(mContext,
+ R.dimen.notification_max_height_for_promoted_ongoing);
mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_heads_up_height_legacy);
mMaxHeadsUpHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
@@ -2757,6 +2789,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (mIsSummaryWithChildren && !shouldShowPublic()) {
return !mChildrenExpanded;
}
+ if (isPromotedOngoing()) {
+ return false;
+ }
return mEnableNonGroupedNotificationExpand && mExpandable;
}
@@ -2765,6 +2800,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mPrivateLayout.updateExpandButtons(isExpandable());
}
+ /**
+ * Set this notification to be promoted ongoing
+ */
+ public void setPromotedOngoing(boolean promotedOngoing) {
+ if (PromotedNotificationUiForceExpanded.isUnexpectedlyInLegacyMode()) {
+ return;
+ }
+
+ mIsPromotedOngoing = promotedOngoing;
+ setExpandable(!mIsPromotedOngoing);
+ }
+
@Override
public void setClipToActualHeight(boolean clipToActualHeight) {
super.setClipToActualHeight(clipToActualHeight || isUserLocked());
@@ -2834,6 +2881,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
public void setUserLocked(boolean userLocked) {
+ if (isPromotedOngoing()) return;
+
mUserLocked = userLocked;
mPrivateLayout.setUserExpanding(userLocked);
// This is intentionally not guarded with mIsSummaryWithChildren since we might have had
@@ -2995,6 +3044,35 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
+ public boolean isPromotedOngoing() {
+ return PromotedNotificationUiForceExpanded.isEnabled() && mIsPromotedOngoing;
+ }
+
+ private boolean isPromotedNotificationExpanded(boolean allowOnKeyguard) {
+ // public view in non group notifications is always collapsed.
+ if (shouldShowPublic()) {
+ return false;
+ }
+ // RON will always be expanded when it is not on keyguard.
+ if (!mOnKeyguard) {
+ return true;
+ }
+ // RON will always be expanded when it is allowed on keyguard.
+ // allowOnKeyguard is used for getting the maximum height by NotificationContentView and
+ // NotificationChildrenContainer.
+ if (allowOnKeyguard) {
+ return true;
+ }
+
+ // RON will be expanded when it needs to ignore lockscreen constraints.
+ if (mIgnoreLockscreenConstraints) {
+ return true;
+ }
+
+ // RON will need be collapsed when it needs to save space on the lock screen.
+ return !mSaveSpaceOnLockscreen;
+ }
+
/**
* Check whether the view state is currently expanded. This is given by the system in {@link
* #setSystemExpanded(boolean)} and can be overridden by user expansion or
@@ -3008,6 +3086,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
public boolean isExpanded(boolean allowOnKeyguard) {
+ if (isPromotedOngoing()) {
+ return isPromotedNotificationExpanded(allowOnKeyguard);
+ }
+
return (!shouldShowPublic()) && (!mOnKeyguard || allowOnKeyguard)
&& (!hasUserChangedExpansion()
&& (isSystemExpanded() || isSystemChildExpanded())
@@ -3179,7 +3261,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public boolean mustStayOnScreen() {
- return mIsHeadsUp && mMustStayOnScreen;
+ // Must stay on screen in the open shade regardless how much the stack is scrolled if:
+ // 1. Is HUN and not marked as seen yet (isHeadsUp && mustStayOnScreen)
+ // 2. Is an FSI HUN (isPinned)
+ return mIsHeadsUp && mMustStayOnScreen || notificationsPinnedHunInShade() && isPinned();
}
/**
@@ -4006,6 +4091,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
+ (!shouldShowPublic() && mIsSummaryWithChildren));
pw.print(", mShowNoBackground: " + mShowNoBackground);
pw.print(", clipBounds: " + getClipBounds());
+ if (PromotedNotificationUiForceExpanded.isEnabled()) {
+ pw.print(", isPromotedOngoing: " + isPromotedOngoing());
+ pw.print(", isExpandable: " + isExpandable());
+ pw.print(", mExpandable: " + mExpandable);
+ }
pw.println();
if (NotificationContentView.INCLUDE_HEIGHTS_TO_DUMP) {
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 e06280e36bc8..626230353bd7 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
@@ -106,6 +106,7 @@ public class ExpandableNotificationRowController implements NotifViewController
private final NotificationGutsManager mNotificationGutsManager;
private final OnUserInteractionCallback mOnUserInteractionCallback;
private final FalsingManager mFalsingManager;
+ private final NotificationRebindingTracker mNotificationRebindingTracker;
private final FeatureFlagsClassic mFeatureFlags;
private final boolean mAllowLongPress;
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@@ -275,7 +276,8 @@ public class ExpandableNotificationRowController implements NotifViewController
NotificationDismissibilityProvider dismissibilityProvider,
IStatusBarService statusBarService,
UiEventLogger uiEventLogger,
- MSDLPlayer msdlPlayer) {
+ MSDLPlayer msdlPlayer,
+ NotificationRebindingTracker notificationRebindingTracker) {
mView = view;
mListContainer = listContainer;
mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory;
@@ -295,6 +297,7 @@ public class ExpandableNotificationRowController implements NotifViewController
mNotificationGutsManager = notificationGutsManager;
mOnUserInteractionCallback = onUserInteractionCallback;
mFalsingManager = falsingManager;
+ mNotificationRebindingTracker = notificationRebindingTracker;
mOnFeedbackClickListener = mNotificationGutsManager::openGuts;
mAllowLongPress = allowLongPress;
mFeatureFlags = featureFlags;
@@ -343,7 +346,8 @@ public class ExpandableNotificationRowController implements NotifViewController
mSmartReplyConstants,
mSmartReplyController,
mStatusBarService,
- mUiEventLogger
+ mUiEventLogger,
+ mNotificationRebindingTracker
);
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
if (mAllowLongPress) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 57fe24f40acb..c0dbb37c1b36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -57,6 +57,7 @@ import com.android.systemui.statusbar.notification.ConversationNotificationProce
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation;
@@ -1111,6 +1112,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder
entry.setHeadsUpStatusBarText(result.headsUpStatusBarText);
entry.setHeadsUpStatusBarTextPublic(result.headsUpStatusBarTextPublic);
+ if (PromotedNotificationUiForceExpanded.isEnabled()) {
+ row.setPromotedOngoing(entry.isOngoingPromoted());
+ }
+
Trace.endAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row));
if (endListener != null) {
endListener.onAsyncInflationFinished(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRebindingTracker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRebindingTracker.kt
new file mode 100644
index 000000000000..40dbdf27c920
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRebindingTracker.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.row
+
+import com.android.app.tracing.FlowTracing.traceEach
+import com.android.app.tracing.TraceUtils.traceAsyncClosable
+import com.android.app.tracing.TrackGroupUtils.trackGroup
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+
+/**
+ * Tracks notification rebindings in progress as a result of a configuration change (such as density
+ * or font size)
+ */
+@SysUISingleton
+class NotificationRebindingTracker
+@Inject
+constructor(
+ activeNotificationsInteractor: ActiveNotificationsInteractor,
+ @Background private val bgScope: CoroutineScope,
+ @Application private val appScope: CoroutineScope,
+) : CoreStartable {
+
+ private val rebindingKeys = MutableStateFlow(emptySet<String>())
+ private val activeKeys: Flow<Set<String>> =
+ activeNotificationsInteractor.allRepresentativeNotifications
+ .map { notifications: Map<String, *> ->
+ notifications.map { (notifKey, _) -> notifKey }.toSet()
+ }
+ .traceEach(trackGroup("shade", "activeKeys"))
+
+ /**
+ * Emits the current number of active notification rebinding in progress.
+ *
+ * Note the usaged of the [appScope] instead of the bg one is intentional, as we need the value
+ * immediately also in the same frame if it changes.
+ */
+ val rebindingInProgressCount: StateFlow<Int> =
+ rebindingKeys
+ .map { it.size }
+ .traceEach(trackGroup("shade", "rebindingInProgressCount"), traceEmissionCount = true)
+ .stateIn(appScope, started = SharingStarted.Eagerly, initialValue = 0)
+
+ override fun start() {
+ syncRebindingKeysWithActiveKeys()
+ }
+
+ private fun syncRebindingKeysWithActiveKeys() {
+ // Let's make sure that the "rebindingKeys" set doesn't contain entries that are not active
+ // anymore.
+ bgScope.launch {
+ activeKeys.collect { activeKeys ->
+ rebindingKeys.update { currentlyBeingInflated ->
+ currentlyBeingInflated.intersect(activeKeys)
+ }
+ }
+ }
+ }
+
+ /** Should be called when the inflation begins */
+ fun trackRebinding(key: String): RebindFinishedCallback {
+ val endTrace =
+ traceAsyncClosable(
+ trackGroupName = "Notifications",
+ trackName = "Rebinding",
+ sliceName = "Rebinding in progress for $key",
+ )
+ rebindingKeys.value += key
+ return RebindFinishedCallback {
+ endTrace()
+ rebindingKeys.value -= key
+ }
+ }
+
+ /**
+ * Callback to notify the end of a rebiding. Views are expected to be in the hierarchy when this
+ * is called.
+ */
+ fun interface RebindFinishedCallback {
+ fun onFinished()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackRebindingHider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackRebindingHider.kt
new file mode 100644
index 000000000000..dba2308ea34b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackRebindingHider.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.stack
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * Allows to hide the content of the notification stack during notification rebinding.
+ *
+ * Note that this is mainly created to allow SystemUI variants without NSSL to provide their own
+ * implementations.
+ */
+interface NotificationStackRebindingHider {
+ /** Sets the Notification stack visibility. */
+ fun setVisible(visible: Boolean, animated: Boolean)
+}
+
+@SysUISingleton
+class NotificationStackRebindingHiderImpl
+@Inject
+constructor(private val nsslController: NotificationStackScrollLayoutController) :
+ NotificationStackRebindingHider {
+ override fun setVisible(visible: Boolean, animated: Boolean) {
+ nsslController.updateContainerVisibilityForRebind(visible, animated)
+ }
+}
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 f57107141f61..bf24c873c693 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
@@ -21,6 +21,7 @@ import static android.os.Trace.TRACE_TAG_APP;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_UP;
+import static com.android.app.tracing.TrackGroupUtils.trackGroup;
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.Flags.notificationOverExpansionClippingFix;
@@ -1395,6 +1396,16 @@ public class NotificationStackScrollLayout
}
}
+ @Override
+ public void setAlpha(float alpha) {
+ super.setAlpha(alpha);
+ if (Trace.isEnabled()) {
+ Trace.setCounter(
+ trackGroup(/* groupName= */ "shade", /* trackName= */ "NSSLResultingAlpha"),
+ (int) (alpha * 100));
+ }
+ }
+
private boolean isCurrentlyAnimating() {
return mStateAnimator.isRunning();
}
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 dc0fae80d041..8eaef3681e5c 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
@@ -249,9 +249,26 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
};
+ private static final Property<NotificationStackScrollLayoutController, Float>
+ HIDE_DURING_REBINDING_PROPERTY = new Property<>(Float.class,
+ "HideNotificationsAlphaDuringRebind") {
+ @Override
+ public Float get(NotificationStackScrollLayoutController object) {
+ return object.mMaxAlphaForRebind;
+ }
+
+ @Override
+ public void set(NotificationStackScrollLayoutController object, Float value) {
+ object.setMaxAlphaForRebind(value);
+ }
+ };
+
@Nullable
private ObjectAnimator mHideAlphaAnimator = null;
+ @Nullable
+ private ObjectAnimator mRebindAlphaAnimator = null;
+
private final Runnable mSensitiveStateChangedListener = new Runnable() {
@Override
public void run() {
@@ -295,6 +312,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
private float mMaxAlphaForKeyguard = 1.0f;
private String mMaxAlphaForKeyguardSource = "constructor";
private float mMaxAlphaForUnhide = 1.0f;
+ private float mMaxAlphaForRebind = 1.0f;
private float mMaxAlphaFromView = 1.0f;
/**
@@ -1244,6 +1262,16 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
/**
+ * Max alpha for rebind.
+ *
+ * Used to hide notifications while rebiding is in progress (e.g. after a density change).
+ */
+ public void setMaxAlphaForRebind(float alpha) {
+ mMaxAlphaForRebind = alpha;
+ updateAlpha();
+ }
+
+ /**
* Applies a blur effect to the view.
*
* @param blurRadius Radius of blur
@@ -1261,8 +1289,10 @@ public class NotificationStackScrollLayoutController implements Dumpable {
private void updateAlpha() {
if (mView != null) {
- mView.setAlpha(Math.min(Math.min(mMaxAlphaFromView, mMaxAlphaForKeyguard),
- Math.min(mMaxAlphaForUnhide, mMaxAlphaForGlanceableHub)));
+ float newAlpha = Math.min(mMaxAlphaForRebind,
+ Math.min(Math.min(mMaxAlphaFromView, mMaxAlphaForKeyguard),
+ Math.min(mMaxAlphaForUnhide, mMaxAlphaForGlanceableHub)));
+ mView.setAlpha(newAlpha);
}
}
@@ -1289,6 +1319,26 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
}
+ /**
+ * Sets whether the nssl should be visible or not. Used during notification rebinding, to hide
+ * possible flickers that happen when display density changes. (e.g. as a result of the shade
+ * moving to a different display.)
+ */
+ public void updateContainerVisibilityForRebind(boolean visible, boolean animate) {
+ if (mRebindAlphaAnimator != null) {
+ mRebindAlphaAnimator.cancel();
+ }
+
+ final float targetAlpha = visible ? 1f : 0f;
+
+ if (animate) {
+ mRebindAlphaAnimator = createAlphaAnimatorForRebind(targetAlpha);
+ mRebindAlphaAnimator.start();
+ } else {
+ HIDE_DURING_REBINDING_PROPERTY.set(this, targetAlpha);
+ }
+ }
+
private ObjectAnimator createAlphaAnimator(float targetAlpha) {
final ObjectAnimator objectAnimator = ObjectAnimator
.ofFloat(this, HIDE_ALPHA_PROPERTY, targetAlpha);
@@ -1297,6 +1347,14 @@ public class NotificationStackScrollLayoutController implements Dumpable {
return objectAnimator;
}
+ private ObjectAnimator createAlphaAnimatorForRebind(float targetAlpha) {
+ final ObjectAnimator objectAnimator = ObjectAnimator
+ .ofFloat(this, HIDE_DURING_REBINDING_PROPERTY, targetAlpha);
+ objectAnimator.setInterpolator(STANDARD);
+ objectAnimator.setDuration(ANIMATION_DURATION_STANDARD);
+ return objectAnimator;
+ }
+
public float calculateAppearFraction(float height) {
SceneContainerFlag.assertInLegacyMode();
return mView.calculateAppearFraction(height);
@@ -1688,6 +1746,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("mMaxAlphaFromView=" + mMaxAlphaFromView);
pw.println("mMaxAlphaForUnhide=" + mMaxAlphaForUnhide);
+ pw.println("mMaxAlphaForRebind=" + mMaxAlphaForRebind);
pw.println("mMaxAlphaForGlanceableHub=" + mMaxAlphaForGlanceableHub);
pw.println("mMaxAlphaForKeyguard=" + mMaxAlphaForKeyguard);
pw.println("mMaxAlphaForKeyguardSource=" + mMaxAlphaForKeyguardSource);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index a96d972af2c4..08bc8f5d5bb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -29,6 +29,7 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
@@ -463,7 +464,12 @@ constructor(
var size =
if (onLockscreen) {
- if (view is ExpandableNotificationRow && view.entry.isStickyAndNotDemoted) {
+ if (
+ view is ExpandableNotificationRow &&
+ (view.entry.isStickyAndNotDemoted ||
+ (PromotedNotificationUiForceExpanded.isEnabled &&
+ view.isPromotedOngoing))
+ ) {
height
} else {
view.getMinHeight(/* ignoreTemporaryStates= */ true).toFloat()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
index 705845ff984c..5d8f06ff51be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
@@ -60,6 +60,7 @@ class SharedNotificationContainer(context: Context, attrs: AttributeSet?) :
marginTop: Int,
marginEnd: Int,
marginBottom: Int,
+ nsslAlpha: Float,
) {
val constraintSet = ConstraintSet()
constraintSet.clone(baseConstraintSet)
@@ -83,6 +84,10 @@ class SharedNotificationContainer(context: Context, attrs: AttributeSet?) :
}
}
+ // Constraint layout sets the alpha to 1 if it's not set explicitly in the constraint
+ // set. Let's keep the current nssl alpha instead, otherwise this might interfere with
+ // animations.
+ setAlpha(nsslId, nsslAlpha)
connect(nsslId, START, startConstraintId, START, marginStart)
if (
!SceneContainerFlag.isEnabled ||
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 3ea4d488357d..1965b9538df0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -76,6 +76,7 @@ constructor(
marginTop = it.marginTop,
marginEnd = it.marginEnd,
marginBottom = it.marginBottom,
+ nsslAlpha = controller.alpha,
)
controller.setOverExpansion(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 16e9c717935c..a2f1ded042f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -26,24 +26,31 @@ import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
-import android.widget.TextView;
import androidx.annotation.StyleRes;
+import androidx.core.graphics.ColorUtils;
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Flags;
import com.android.systemui.keyguard.KeyguardIndication;
import com.android.systemui.res.R;
+import com.android.systemui.shared.shadow.DoubleShadowTextView;
/**
* A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open").
*/
-public class KeyguardIndicationTextView extends TextView {
+public class KeyguardIndicationTextView extends DoubleShadowTextView {
+ // Minimum luminance for texts to receive shadows.
+ private static final float MIN_TEXT_SHADOW_LUMINANCE = 0.5f;
public static final long Y_IN_DURATION = 600L;
@StyleRes
private static int sStyleId = R.style.TextAppearance_Keyguard_BottomArea;
@StyleRes
+ private static int sStyleWithDoubleShadowTextId =
+ R.style.TextAppearance_Keyguard_BottomArea_DoubleShadow;
+ @StyleRes
private static int sButtonStyleId = R.style.TextAppearance_Keyguard_BottomArea_Button;
private boolean mAnimationsEnabled = true;
@@ -226,7 +233,14 @@ public class KeyguardIndicationTextView extends TextView {
if (mKeyguardIndicationInfo.getBackground() != null) {
setTextAppearance(sButtonStyleId);
} else {
- setTextAppearance(sStyleId);
+ // If text is transparent or dark color, don't draw any shadow
+ if (Flags.indicationTextA11yFix() && ColorUtils.calculateLuminance(
+ mKeyguardIndicationInfo.getTextColor().getDefaultColor())
+ > MIN_TEXT_SHADOW_LUMINANCE) {
+ setTextAppearance(sStyleWithDoubleShadowTextId);
+ } else {
+ setTextAppearance(sStyleId);
+ }
}
setBackground(mKeyguardIndicationInfo.getBackground());
setTextColor(mKeyguardIndicationInfo.getTextColor());
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 4c2bfe5ca257..40245aef4f67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -23,6 +23,7 @@ import static com.android.systemui.Flags.updateUserSwitcherBackground;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -56,6 +57,7 @@ import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.shade.ShadeViewStateProvider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
@@ -114,6 +116,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
R.id.keyguard_hun_animator_start_tag);
private final CoroutineDispatcher mCoroutineDispatcher;
+ private final Context mContext;
private final CarrierTextController mCarrierTextController;
private final ConfigurationController mConfigurationController;
private final SystemStatusAnimationScheduler mAnimationScheduler;
@@ -129,7 +132,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
private final KeyguardStatusBarViewModel mKeyguardStatusBarViewModel;
private final BiometricUnlockController mBiometricUnlockController;
private final SysuiStatusBarStateController mStatusBarStateController;
- private final StatusBarContentInsetsProvider mInsetsProvider;
+ private final StatusBarContentInsetsProviderStore mInsetsProviderStore;
private final UserManager mUserManager;
private final StatusBarUserChipViewModel mStatusBarUserChipViewModel;
private final SecureSettings mSecureSettings;
@@ -314,6 +317,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
@Inject
public KeyguardStatusBarViewController(
@Main CoroutineDispatcher dispatcher,
+ @ShadeDisplayAware Context context,
KeyguardStatusBarView view,
CarrierTextController carrierTextController,
ConfigurationController configurationController,
@@ -347,6 +351,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
) {
super(view);
mCoroutineDispatcher = dispatcher;
+ mContext = context;
mCarrierTextController = carrierTextController;
mConfigurationController = configurationController;
mAnimationScheduler = animationScheduler;
@@ -362,7 +367,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
mKeyguardStatusBarViewModel = keyguardStatusBarViewModel;
mBiometricUnlockController = biometricUnlockController;
mStatusBarStateController = statusBarStateController;
- mInsetsProvider = statusBarContentInsetsProviderStore.getDefaultDisplay();
+ mInsetsProviderStore = statusBarContentInsetsProviderStore;
mUserManager = userManager;
mStatusBarUserChipViewModel = userChipViewModel;
mSecureSettings = secureSettings;
@@ -404,6 +409,10 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
mStatusOverlayHoverListenerFactory = statusOverlayHoverListenerFactory;
}
+ private StatusBarContentInsetsProvider insetsProvider() {
+ return mInsetsProviderStore.forDisplay(mContext.getDisplayId());
+ }
+
@Override
protected void onInit() {
super.onInit();
@@ -446,7 +455,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
.createDarkAwareListener(mSystemIconsContainer, mView.darkChangeFlow());
mSystemIconsContainer.setOnHoverListener(hoverListener);
mView.setOnApplyWindowInsetsListener(
- (view, windowInsets) -> mView.updateWindowInsets(windowInsets, mInsetsProvider));
+ (view, windowInsets) -> mView.updateWindowInsets(windowInsets, insetsProvider()));
mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
false,
@@ -645,7 +654,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
* {@code OnApplyWindowInsetsListener}s.
*/
public void setDisplayCutout(@Nullable DisplayCutout displayCutout) {
- mView.setDisplayCutout(displayCutout, mInsetsProvider);
+ mView.setDisplayCutout(displayCutout, insetsProvider());
}
/**
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 1a97ab635028..4825a10e901b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -144,6 +144,7 @@ public class NotificationIconContainer extends ViewGroup {
private int mMaxIcons = Integer.MAX_VALUE;
private boolean mOverrideIconColor;
+ private boolean mUseInverseOverrideIconColor;
private boolean mIsStaticLayout = true;
private final HashMap<View, IconState> mIconStates = new HashMap<>();
private int mDotPadding;
@@ -169,6 +170,7 @@ public class NotificationIconContainer extends ViewGroup {
private final int[] mAbsolutePosition = new int[2];
@Nullable private View mIsolatedIconForAnimation;
private int mThemedTextColorPrimary;
+ private int mThemedTextColorPrimaryInverse;
@Nullable private Runnable mIsolatedIconAnimationEndRunnable;
private boolean mUseIncreasedIconScale;
@@ -191,6 +193,8 @@ public class NotificationIconContainer extends ViewGroup {
com.android.internal.R.style.Theme_DeviceDefault_DayNight);
mThemedTextColorPrimary = Utils.getColorAttr(themedContext,
com.android.internal.R.attr.textColorPrimary).getDefaultColor();
+ mThemedTextColorPrimaryInverse = Utils.getColorAttr(themedContext,
+ com.android.internal.R.attr.textColorPrimaryInverse).getDefaultColor();
}
@Override
@@ -713,6 +717,10 @@ public class NotificationIconContainer extends ViewGroup {
mOverrideIconColor = override;
}
+ public void setUseInverseOverrideIconColor(boolean override) {
+ mUseInverseOverrideIconColor = override;
+ }
+
public class IconState extends ViewState {
public float iconAppearAmount = 1.0f;
public float clampedAppearAmount = 1.0f;
@@ -821,7 +829,9 @@ public class NotificationIconContainer extends ViewGroup {
}
icon.setVisibleState(visibleState, animationsAllowed);
if (mOverrideIconColor) {
- icon.setIconColor(mThemedTextColorPrimary,
+ int overrideIconColor = mUseInverseOverrideIconColor
+ ? mThemedTextColorPrimaryInverse : mThemedTextColorPrimary;
+ icon.setIconColor(overrideIconColor,
/* animate= */ needsCannedAnimation && animationsAllowed);
}
if (animate) {
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 3f44f7bdef90..caf8a43b2aaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -46,7 +46,6 @@ import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
-import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.window.StatusBarWindowStateController
@@ -84,7 +83,7 @@ private constructor(
private val configurationController: ConfigurationController,
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
private val darkIconDispatcher: DarkIconDispatcher,
- private val statusBarContentInsetsProvider: StatusBarContentInsetsProvider,
+ private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
) : ViewController<PhoneStatusBarView>(view) {
@@ -92,6 +91,8 @@ private constructor(
private lateinit var clock: Clock
private lateinit var startSideContainer: View
private lateinit var endSideContainer: View
+ private val statusBarContentInsetsProvider
+ get() = statusBarContentInsetsProviderStore.forDisplay(context.displayId)
private val iconsOnTouchListener =
object : View.OnTouchListener {
@@ -189,11 +190,9 @@ private constructor(
init {
// These should likely be done in `onInit`, not `init`.
mView.setTouchEventHandler(PhoneStatusBarViewTouchHandler())
- mView.setHasCornerCutoutFetcher {
- statusBarContentInsetsProvider.currentRotationHasCornerCutout()
- }
- mView.setInsetsFetcher {
- statusBarContentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()
+ statusBarContentInsetsProvider?.let {
+ mView.setHasCornerCutoutFetcher { it.currentRotationHasCornerCutout() }
+ mView.setInsetsFetcher { it.getStatusBarContentInsetsForCurrentRotation() }
}
mView.init(userChipViewModel)
}
@@ -393,7 +392,7 @@ private constructor(
configurationController,
statusOverlayHoverListenerFactory,
darkIconDispatcher,
- statusBarContentInsetsProviderStore.defaultDisplay,
+ statusBarContentInsetsProviderStore,
lazyStatusBarShadeDisplayPolicy,
)
}
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 2cd8eafcdb54..0f6c3069609e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -868,6 +868,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
* bounds instead.
*/
public void setClipsQsScrim(boolean clipScrim) {
+ if (Flags.notificationShadeBlur()) {
+ // Never clip scrims when blur is enabled, colors of UI elements are supposed to "add"
+ // up across the scrims.
+ mClipsQsScrim = false;
+ return;
+ }
if (clipScrim == mClipsQsScrim) {
return;
}
@@ -957,11 +963,20 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mBehindAlpha = 1;
mNotificationsAlpha = behindFraction * mDefaultScrimAlpha;
} else {
- mBehindAlpha = mLargeScreenShadeInterpolator.getBehindScrimAlpha(
- mPanelExpansionFraction * mDefaultScrimAlpha);
- mNotificationsAlpha =
- mLargeScreenShadeInterpolator.getNotificationScrimAlpha(
- mPanelExpansionFraction);
+ if (Flags.notificationShadeBlur()) {
+ // TODO (b/390730594): match any spec for controlling alpha based on shade
+ // expansion fraction.
+ mBehindAlpha = mState.getBehindAlpha() * mPanelExpansionFraction;
+ mBehindTint = mState.getBehindTint();
+ mNotificationsAlpha = mState.getNotifAlpha() * mPanelExpansionFraction;
+ mNotificationsTint = mState.getNotifTint();
+ } else {
+ mBehindAlpha = mLargeScreenShadeInterpolator.getBehindScrimAlpha(
+ mPanelExpansionFraction * mDefaultScrimAlpha);
+ mNotificationsAlpha =
+ mLargeScreenShadeInterpolator.getNotificationScrimAlpha(
+ mPanelExpansionFraction);
+ }
}
mBehindTint = mState.getBehindTint();
mInFrontAlpha = 0;
@@ -1015,7 +1030,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
.saturate(mTransitionToLockScreenFullShadeNotificationsProgress);
} else if (mState == ScrimState.SHADE_LOCKED) {
// going from KEYGUARD to SHADE_LOCKED state
- mNotificationsAlpha = getInterpolatedFraction();
+ if (Flags.notificationShadeBlur()) {
+ mNotificationsAlpha = mState.getNotifAlpha() * getInterpolatedFraction();
+ } else {
+ mNotificationsAlpha = getInterpolatedFraction();
+ }
} else if (mState == ScrimState.GLANCEABLE_HUB
&& mTransitionToFullShadeProgress == 0.0f) {
// Notification scrim should not be visible on the glanceable hub unless the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 14937295051d..8170e6d91a0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -26,6 +26,7 @@ import com.android.systemui.Flags;
import com.android.systemui.dock.DockManager;
import com.android.systemui.res.R;
import com.android.systemui.scrim.ScrimView;
+import com.android.systemui.shade.ui.ShadeColors;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
@@ -86,16 +87,24 @@ public enum ScrimState {
} else {
mAnimationDuration = ScrimController.ANIMATION_DURATION;
}
- mFrontTint = mBackgroundColor;
- mBehindTint = mBackgroundColor;
- mNotifTint = mClipQsScrim ? mBackgroundColor : Color.TRANSPARENT;
-
- mFrontAlpha = 0;
- mBehindAlpha = mClipQsScrim ? 1 : mScrimBehindAlphaKeyguard;
- mNotifAlpha = mClipQsScrim ? mScrimBehindAlphaKeyguard : 0;
- if (mClipQsScrim) {
- updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor);
+ if (Flags.notificationShadeBlur()) {
+ mBehindTint = Color.TRANSPARENT;
+ mNotifTint = ShadeColors.notificationScrim(mScrimBehind.getResources());
+ mBehindAlpha = 0.0f;
+ mNotifAlpha = 0.0f;
+ mFrontAlpha = 0.0f;
+ } else {
+ mFrontTint = mBackgroundColor;
+ mBehindTint = mBackgroundColor;
+ mNotifTint = mClipQsScrim ? mBackgroundColor : Color.TRANSPARENT;
+ mFrontAlpha = 0;
+ mBehindAlpha = mClipQsScrim ? 1 : mScrimBehindAlphaKeyguard;
+ mNotifAlpha = mClipQsScrim ? mScrimBehindAlphaKeyguard : 0;
+ if (mClipQsScrim) {
+ updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor);
+ }
}
+
}
},
@@ -169,13 +178,21 @@ public enum ScrimState {
@Override
public void prepare(ScrimState previousState) {
- mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha;
- mNotifAlpha = 1f;
- mFrontAlpha = 0f;
- mBehindTint = mClipQsScrim ? Color.TRANSPARENT : mBackgroundColor;
+ if (Flags.notificationShadeBlur()) {
+ mBehindTint = ShadeColors.shadePanel(mScrimBehind.getResources());
+ mBehindAlpha = Color.alpha(mBehindTint) / 255.0f;
+ mNotifTint = ShadeColors.notificationScrim(mScrimBehind.getResources());
+ mNotifAlpha = Color.alpha(mNotifTint) / 255.0f;
+ mFrontAlpha = 0.0f;
+ } else {
+ mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha;
+ mNotifAlpha = 1f;
+ mFrontAlpha = 0f;
+ mBehindTint = mClipQsScrim ? Color.TRANSPARENT : mBackgroundColor;
- if (mClipQsScrim) {
- updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor);
+ if (mClipQsScrim) {
+ updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor);
+ }
}
}
},
@@ -282,6 +299,13 @@ public enum ScrimState {
mFrontTint = mBackgroundColor;
mBehindTint = mBackgroundColor;
mBlankScreen = true;
+ } else if (Flags.notificationShadeBlur()) {
+ mBehindTint = ShadeColors.shadePanel(mScrimBehind.getResources());
+ mBehindAlpha = Color.alpha(mBehindTint) / 255.0f;
+ mNotifTint = ShadeColors.notificationScrim(mScrimBehind.getResources());
+ mNotifAlpha = Color.alpha(mNotifTint) / 255.0f;
+ mFrontAlpha = 0.0f;
+ return;
}
if (mClipQsScrim) {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/SwitchAppsGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/SwitchAppsGestureRecognizer.kt
index cc382d0a6148..6732287fe256 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/SwitchAppsGestureRecognizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/SwitchAppsGestureRecognizer.kt
@@ -18,12 +18,9 @@ package com.android.systemui.touchpad.tutorial.ui.gesture
import android.util.MathUtils
import android.view.MotionEvent
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection.LEFT
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection.RIGHT
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
-import kotlin.math.abs
-/** Recognizes Quickswitch gesture i.e. using four fingers on touchpad, swiping right. */
+/** Recognizes Quickswitch gesture i.e. using four fingers on touchpad, swiping to right. */
class SwitchAppsGestureRecognizer(private val gestureDistanceThresholdPx: Int) : GestureRecognizer {
private val distanceTracker = DistanceTracker()
@@ -50,13 +47,7 @@ class SwitchAppsGestureRecognizer(private val gestureDistanceThresholdPx: Int) :
gestureStateChangedCallback,
gestureState,
isFinished = { it.deltaX >= gestureDistanceThresholdPx },
- progress = ::getProgress,
+ progress = { InProgress(MathUtils.saturate(it.deltaX / gestureDistanceThresholdPx)) },
)
}
-
- private fun getProgress(it: Moving): InProgress {
- val direction = if (it.deltaX > 0) RIGHT else LEFT
- val value = MathUtils.saturate(abs(it.deltaX / gestureDistanceThresholdPx))
- return InProgress(value, direction)
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureRecognizerProvider.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureRecognizerProvider.kt
index b1e163b55377..8b8606a119d4 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureRecognizerProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/SwitchAppsGestureRecognizerProvider.kt
@@ -18,15 +18,13 @@ package com.android.systemui.touchpad.tutorial.ui.viewmodel
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
import com.android.systemui.touchpad.tutorial.ui.gesture.SwitchAppsGestureRecognizer
-import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
class SwitchAppsGestureRecognizerProvider
@Inject
-constructor(val resources: TouchpadGestureResources, val velocityTracker: VelocityTracker) :
- GestureRecognizerProvider {
+constructor(val resources: TouchpadGestureResources) : GestureRecognizerProvider {
override val recognizer: Flow<GestureRecognizer> =
resources.distanceThreshold().map {
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/GradientColorWallpaper.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/GradientColorWallpaper.kt
index c1fb0e80cafc..760e94c72f19 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/GradientColorWallpaper.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/GradientColorWallpaper.kt
@@ -16,6 +16,7 @@
package com.android.systemui.wallpapers
+import android.app.Flags
import android.graphics.Canvas
import android.graphics.Paint
import android.service.wallpaper.WallpaperService
@@ -26,7 +27,15 @@ import androidx.core.graphics.toRectF
/** A wallpaper that shows a static gradient color image wallpaper. */
class GradientColorWallpaper : WallpaperService() {
- override fun onCreateEngine(): Engine = GradientColorWallpaperEngine()
+ override fun onCreateEngine(): Engine =
+ if (Flags.enableConnectedDisplaysWallpaper()) {
+ GradientColorWallpaperEngine()
+ } else {
+ EmptyWallpaperEngine()
+ }
+
+ /** Empty engine used when the feature flag is disabled. */
+ inner class EmptyWallpaperEngine : Engine()
inner class GradientColorWallpaperEngine : Engine() {
init {
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
index 9055d18a9f55..8487ee751948 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
@@ -23,6 +23,7 @@ import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.flowOf
/**
* A no-op implementation of [WallpaperRepository].
@@ -33,6 +34,6 @@ import kotlinx.coroutines.flow.asStateFlow
@SysUISingleton
class NoopWallpaperRepository @Inject constructor() : WallpaperRepository {
override val wallpaperInfo: StateFlow<WallpaperInfo?> = MutableStateFlow(null).asStateFlow()
- override val wallpaperSupportsAmbientMode = MutableStateFlow(false).asStateFlow()
+ override val wallpaperSupportsAmbientMode = flowOf(false)
override var rootView: View? = null
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
index 015b480eddc8..ed43f8323c31 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
@@ -25,6 +25,8 @@ import android.os.Bundle
import android.os.UserHandle
import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.internal.R
import com.android.systemui.Flags
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
@@ -47,10 +49,10 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
/** A repository storing information about the current wallpaper. */
@@ -59,7 +61,7 @@ interface WallpaperRepository {
val wallpaperInfo: StateFlow<WallpaperInfo?>
/** Emits true if the current user's current wallpaper supports ambient mode. */
- val wallpaperSupportsAmbientMode: StateFlow<Boolean>
+ val wallpaperSupportsAmbientMode: Flow<Boolean>
/** Set rootView to get its windowToken afterwards */
var rootView: View?
@@ -78,9 +80,6 @@ constructor(
private val wallpaperManager: WallpaperManager,
context: Context,
) : WallpaperRepository {
- private val deviceSupportsAodWallpaper =
- context.resources.getBoolean(com.android.internal.R.bool.config_dozeSupportsAodWallpaper)
-
private val wallpaperChanged: Flow<Unit> =
broadcastDispatcher
.broadcastFlow(IntentFilter(Intent.ACTION_WALLPAPER_CHANGED), user = UserHandle.ALL)
@@ -121,7 +120,7 @@ constructor(
)
override val wallpaperInfo: StateFlow<WallpaperInfo?> =
- if (!wallpaperManager.isWallpaperSupported || !deviceSupportsAodWallpaper) {
+ if (!wallpaperManager.isWallpaperSupported) {
MutableStateFlow(null).asStateFlow()
} else {
combine(wallpaperChanged, selectedUser, ::Pair)
@@ -136,25 +135,8 @@ constructor(
)
}
- override val wallpaperSupportsAmbientMode: StateFlow<Boolean> =
- wallpaperInfo
- .map {
- if (ambientAod()) {
- // Force this mode for now, until ImageWallpaper supports it directly
- // TODO(b/371236225)
- true
- } else {
- // If WallpaperInfo is null, it's ImageWallpaper which never supports ambient
- // mode.
- it?.supportsAmbientMode() == true
- }
- }
- .stateIn(
- scope,
- // Always be listening for wallpaper changes.
- SharingStarted.Eagerly,
- initialValue = if (ambientAod()) true else false,
- )
+ override val wallpaperSupportsAmbientMode: Flow<Boolean> =
+ flowOf(context.resources.getBoolean(R.bool.config_dozeSupportsAodWallpaper) && ambientAod())
override var rootView: View? = null
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
index 88795cada716..1b15832d4913 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
@@ -18,9 +18,9 @@ package com.android.systemui.wallpapers.domain.interactor
import com.android.systemui.wallpapers.data.repository.WallpaperRepository
import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
class WallpaperInteractor @Inject constructor(val wallpaperRepository: WallpaperRepository) {
- val wallpaperSupportsAmbientMode: StateFlow<Boolean> =
+ val wallpaperSupportsAmbientMode: Flow<Boolean> =
wallpaperRepository.wallpaperSupportsAmbientMode
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperViewModel.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperViewModel.kt
index a51acf66432a..197f2b010a97 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperViewModel.kt
@@ -18,8 +18,8 @@ package com.android.systemui.wallpapers.ui.viewmodel
import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor
import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
class WallpaperViewModel @Inject constructor(interactor: WallpaperInteractor) {
- val wallpaperSupportsAmbientMode: StateFlow<Boolean> = interactor.wallpaperSupportsAmbientMode
+ val wallpaperSupportsAmbientMode: Flow<Boolean> = interactor.wallpaperSupportsAmbientMode
}
diff --git a/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
index d2069cfdfdc6..dbccc1d8cca4 100644
--- a/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
@@ -49,7 +49,6 @@ object WindowRootViewBinder {
view.repeatWhenAttached {
Log.d(TAG, "Binding root view")
var frameCallbackPendingExecution: FrameCallback? = null
- val viewRootImpl = view.rootView.viewRootImpl
view.viewModel(
minWindowLifecycleState = WindowLifecycleState.ATTACHED,
factory = { viewModelFactory.create() },
@@ -64,13 +63,13 @@ object WindowRootViewBinder {
val newFrameCallback = FrameCallback {
frameCallbackPendingExecution = null
blurUtils.applyBlur(
- viewRootImpl,
+ view.rootView?.viewRootImpl,
blurState.radius,
blurState.isOpaque,
)
viewModel.onBlurApplied(blurState.radius)
}
- blurUtils.prepareBlur(viewRootImpl, blurState.radius)
+ blurUtils.prepareBlur(view.rootView?.viewRootImpl, blurState.radius)
if (frameCallbackPendingExecution != null) {
choreographer.removeFrameCallback(frameCallbackPendingExecution)
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 2645811fa4ad..312d2ffd74e4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -38,6 +38,7 @@ import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELL
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_STOPPED;
import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
+import static com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
@@ -139,6 +140,7 @@ import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig;
import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigImpl;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
@@ -180,6 +182,9 @@ import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -189,9 +194,6 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper
@@ -304,6 +306,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
private JavaAdapter mJavaAdapter;
@Mock
private SceneInteractor mSceneInteractor;
+ @Mock
+ private CommunalSceneInteractor mCommunalSceneInteractor;
@Captor
private ArgumentCaptor<FaceAuthenticationListener> mFaceAuthenticationListener;
@@ -1084,6 +1088,49 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ public void udfpsStopsListeningWhenCommunalShowing() {
+ // GIVEN keyguard showing
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isTrue();
+
+ // WHEN communal is shown
+ mKeyguardUpdateMonitor.onCommunalShowingChanged(true);
+
+ // THEN shouldn't listen for fingerprint
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isFalse();
+
+ // WHEN alternate bouncer shows on top of communal, we should listen for fingerprint
+ mKeyguardUpdateMonitor.setAlternateBouncerVisibility(true);
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isTrue();
+
+ // WHEN communal is hidden
+ mKeyguardUpdateMonitor.onCommunalShowingChanged(false);
+ mKeyguardUpdateMonitor.setAlternateBouncerVisibility(false);
+
+ // THEN listen for fingerprint
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isTrue();
+ }
+
+ @Test
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ public void sfpsNotAffectedByCommunalShowing() {
+ // GIVEN keyguard showing
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
+
+ // WHEN communal is shown
+ mKeyguardUpdateMonitor.onCommunalShowingChanged(true);
+
+ // THEN we should still listen for fingerprint if not UDFPS
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
+ }
+
+ @Test
public void testFingerprintPowerPressed_restartsFingerprintListeningStateWithDelay() {
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
.onAuthenticationError(FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED, "");
@@ -2669,7 +2716,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTaskStackChangeListeners, mSelectedUserInteractor, mActivityTaskManager,
() -> mAlternateBouncerInteractor,
() -> mJavaAdapter,
- () -> mSceneInteractor);
+ () -> mSceneInteractor,
+ mCommunalSceneInteractor);
setAlternateBouncerVisibility(false);
setPrimaryBouncerVisibility(false);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index 909680866d20..4e1ccfb07220 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -21,14 +21,11 @@ import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.os.PowerManager
-import android.os.Process
-import android.os.UserHandle
import android.os.UserManager
import android.testing.TestableContext
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.internal.app.AssistUtils
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
@@ -42,6 +39,7 @@ import com.android.systemui.model.SysUiState
import com.android.systemui.model.sceneContainerPlugin
import com.android.systemui.navigationbar.NavigationBarController
import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.process.ProcessWrapper
import com.android.systemui.recents.OverviewProxyService.ACTION_QUICKSTEP
import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.settings.UserTracker
@@ -92,6 +90,7 @@ class OverviewProxyServiceTest : SysuiTestCase() {
private val kosmos = testKosmos()
private lateinit var subject: OverviewProxyService
@Mock private val dumpManager = DumpManager()
+ @Mock private val processWrapper = ProcessWrapper()
private val displayTracker = FakeDisplayTracker(mContext)
private val fakeSystemClock = FakeSystemClock()
private val sysUiState = SysUiState(displayTracker, kosmos.sceneContainerPlugin)
@@ -146,6 +145,8 @@ class OverviewProxyServiceTest : SysuiTestCase() {
mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ // return isSystemUser as true by default.
+ `when`(processWrapper.isSystemUser).thenReturn(true)
subject = createOverviewProxyService(context)
}
@@ -202,68 +203,44 @@ class OverviewProxyServiceTest : SysuiTestCase() {
@Test
fun connectToOverviewService_primaryUserNoVisibleBgUsersSupported_expectBindService() {
- val mockitoSession =
- ExtendedMockito.mockitoSession().spyStatic(Process::class.java).startMocking()
- try {
- `when`(Process.myUserHandle()).thenReturn(UserHandle.SYSTEM)
- `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(false)
- val spyContext = spy(context)
- val ops = createOverviewProxyService(spyContext)
- ops.startConnectionToCurrentUser()
- verify(spyContext, atLeast(1)).bindServiceAsUser(any(), any(), anyInt(), any())
- } finally {
- mockitoSession.finishMocking()
- }
+ `when`(processWrapper.isSystemUser).thenReturn(true)
+ `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(false)
+ val spyContext = spy(context)
+ val ops = createOverviewProxyService(spyContext)
+ ops.startConnectionToCurrentUser()
+ verify(spyContext, atLeast(1)).bindServiceAsUser(any(), any(), anyInt(), any())
}
@Test
fun connectToOverviewService_nonPrimaryUserNoVisibleBgUsersSupported_expectNoBindService() {
- val mockitoSession =
- ExtendedMockito.mockitoSession().spyStatic(Process::class.java).startMocking()
- try {
- `when`(Process.myUserHandle()).thenReturn(UserHandle.of(12345))
- `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(false)
- val spyContext = spy(context)
- val ops = createOverviewProxyService(spyContext)
- ops.startConnectionToCurrentUser()
- verify(spyContext, times(0)).bindServiceAsUser(any(), any(), anyInt(), any())
- } finally {
- mockitoSession.finishMocking()
- }
+ `when`(processWrapper.isSystemUser).thenReturn(false)
+ `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(false)
+ val spyContext = spy(context)
+ val ops = createOverviewProxyService(spyContext)
+ ops.startConnectionToCurrentUser()
+ verify(spyContext, times(0)).bindServiceAsUser(any(), any(), anyInt(), any())
}
@Test
fun connectToOverviewService_nonPrimaryBgUserVisibleBgUsersSupported_expectBindService() {
- val mockitoSession =
- ExtendedMockito.mockitoSession().spyStatic(Process::class.java).startMocking()
- try {
- `when`(Process.myUserHandle()).thenReturn(UserHandle.of(12345))
- `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(true)
- `when`(userManager.isUserForeground()).thenReturn(false)
- val spyContext = spy(context)
- val ops = createOverviewProxyService(spyContext)
- ops.startConnectionToCurrentUser()
- verify(spyContext, atLeast(1)).bindServiceAsUser(any(), any(), anyInt(), any())
- } finally {
- mockitoSession.finishMocking()
- }
+ `when`(processWrapper.isSystemUser).thenReturn(false)
+ `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(true)
+ `when`(userManager.isUserForeground()).thenReturn(false)
+ val spyContext = spy(context)
+ val ops = createOverviewProxyService(spyContext)
+ ops.startConnectionToCurrentUser()
+ verify(spyContext, atLeast(1)).bindServiceAsUser(any(), any(), anyInt(), any())
}
@Test
fun connectToOverviewService_nonPrimaryFgUserVisibleBgUsersSupported_expectNoBindService() {
- val mockitoSession =
- ExtendedMockito.mockitoSession().spyStatic(Process::class.java).startMocking()
- try {
- `when`(Process.myUserHandle()).thenReturn(UserHandle.of(12345))
- `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(true)
- `when`(userManager.isUserForeground()).thenReturn(true)
- val spyContext = spy(context)
- val ops = createOverviewProxyService(spyContext)
- ops.startConnectionToCurrentUser()
- verify(spyContext, times(0)).bindServiceAsUser(any(), any(), anyInt(), any())
- } finally {
- mockitoSession.finishMocking()
- }
+ `when`(processWrapper.isSystemUser).thenReturn(false)
+ `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(true)
+ `when`(userManager.isUserForeground()).thenReturn(true)
+ val spyContext = spy(context)
+ val ops = createOverviewProxyService(spyContext)
+ ops.startConnectionToCurrentUser()
+ verify(spyContext, times(0)).bindServiceAsUser(any(), any(), anyInt(), any())
}
private fun createOverviewProxyService(ctx: Context): OverviewProxyService {
@@ -292,6 +269,7 @@ class OverviewProxyServiceTest : SysuiTestCase() {
unfoldTransitionProgressForwarder,
broadcastDispatcher,
backAnimation,
+ processWrapper,
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
index eae828562223..1d348b13a481 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -53,6 +53,7 @@ import com.android.systemui.shade.ShadeHeaderController.Companion.QQS_HEADER_CON
import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
import com.android.systemui.shade.carrier.ShadeCarrierGroup
import com.android.systemui.shade.carrier.ShadeCarrierGroupController
+import com.android.systemui.shade.data.repository.shadeDisplaysRepository
import com.android.systemui.statusbar.data.repository.fakeStatusBarContentInsetsProviderStore
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.StatusOverlayHoverListenerFactory
@@ -96,7 +97,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val insetsProviderStore = kosmos.fakeStatusBarContentInsetsProviderStore
- private val insetsProvider = insetsProviderStore.defaultDisplay
+ private val insetsProvider = insetsProviderStore.forDisplay(context.displayId)
@Mock(answer = Answers.RETURNS_MOCKS) private lateinit var view: MotionLayout
@Mock private lateinit var statusIcons: StatusIconContainer
@@ -196,6 +197,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() {
privacyIconsController,
insetsProviderStore,
configurationController,
+ kosmos.shadeDisplaysRepository,
variableDateViewControllerFactory,
batteryMeterViewController,
dumpManager,
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 5aee92939ed5..493468e8f675 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
@@ -69,6 +69,7 @@ import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
@@ -878,6 +879,85 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+ public void isExpanded_sensitivePromotedNotification_notExpanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setPromotedOngoing(true);
+ row.setSensitive(/* sensitive= */true, /* hideSensitive= */false);
+ row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */true);
+
+ // THEN
+ assertThat(row.isExpanded()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+ public void isExpanded_promotedNotificationNotOnKeyguard_expanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setPromotedOngoing(true);
+ row.setOnKeyguard(false);
+
+ // THEN
+ assertThat(row.isExpanded()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+ public void isExpanded_promotedNotificationAllowOnKeyguard_expanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setPromotedOngoing(true);
+ row.setOnKeyguard(true);
+
+ // THEN
+ assertThat(row.isExpanded(/* allowOnKeyguard = */ true)).isTrue();
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+ public void isExpanded_promotedNotificationIgnoreLockscreenConstraints_expanded()
+ throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setPromotedOngoing(true);
+ row.setOnKeyguard(true);
+ row.setIgnoreLockscreenConstraints(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+ public void isExpanded_promotedNotificationSaveSpaceOnLockScreen_notExpanded()
+ throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setPromotedOngoing(true);
+ row.setOnKeyguard(true);
+ row.setSaveSpaceOnLockscreen(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+ public void isExpanded_promotedNotificationNotSaveSpaceOnLockScreen_expanded()
+ throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setPromotedOngoing(true);
+ row.setOnKeyguard(true);
+ row.setSaveSpaceOnLockscreen(false);
+
+ // THEN
+ assertThat(row.isExpanded()).isTrue();
+ }
+
+ @Test
public void onDisappearAnimationFinished_shouldSetFalse_headsUpAnimatingAway()
throws Exception {
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
@@ -941,6 +1021,57 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
assertThat(row.getImageResolver().getContext()).isSameInstanceAs(userContext);
}
+ @Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_NOTIFICATIONS_PINNED_HUN_IN_SHADE)
+ public void mustStayOnScreen_false() throws Exception {
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ assertThat(row.mustStayOnScreen()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_NOTIFICATIONS_PINNED_HUN_IN_SHADE)
+ public void mustStayOnScreen_isHeadsUp_markedAsSeen() throws Exception {
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ // When the row is a HUN
+ row.setHeadsUp(true);
+ //Then it must stay on screen
+ assertThat(row.mustStayOnScreen()).isTrue();
+ // And when the user has seen it
+ row.markHeadsUpSeen();
+ // Then it should NOT stay on screen anymore
+ assertThat(row.mustStayOnScreen()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_NOTIFICATIONS_PINNED_HUN_IN_SHADE)
+ public void mustStayOnScreen_isPinned_markedAsSeen() throws Exception {
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ // When a HUN is pinned
+ row.setHeadsUp(true);
+ row.setPinnedStatus(PinnedStatus.PinnedBySystem);
+ //Then it must stay on screen
+ assertThat(row.mustStayOnScreen()).isTrue();
+ // And when the user has seen it
+ row.markHeadsUpSeen();
+ // Then it should still stay on screen
+ assertThat(row.mustStayOnScreen()).isTrue();
+ }
+
+ @Test
+ @DisableFlags(com.android.systemui.Flags.FLAG_NOTIFICATIONS_PINNED_HUN_IN_SHADE)
+ public void mustStayOnScreen_isPinned_markedAsSeen_false() throws Exception {
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ // When a HUN is pinned
+ row.setHeadsUp(true);
+ row.setPinnedStatus(PinnedStatus.PinnedBySystem);
+ //Then it must stay on screen
+ assertThat(row.mustStayOnScreen()).isTrue();
+ // And when the user has seen it
+ row.markHeadsUpSeen();
+ // Then it should NOT stay on screen anymore
+ assertThat(row.mustStayOnScreen()).isFalse();
+ }
+
private void setDrawableIconsInImageView(CachingIconView icon, Drawable iconDrawable,
Drawable rightIconDrawable) {
ImageView iconView = mock(ImageView.class);
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 437ccb6a9821..68f66611c981 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
@@ -90,6 +90,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val statusBarContentInsetsProviderStore = kosmos.fakeStatusBarContentInsetsProviderStore
private val statusBarContentInsetsProvider = statusBarContentInsetsProviderStore.defaultDisplay
+ private val statusBarContentInsetsProviderForSecondaryDisplay =
+ statusBarContentInsetsProviderStore.forDisplay(SECONDARY_DISPLAY_ID)
private val fakeDarkIconDispatcher = kosmos.fakeDarkIconDispatcher
@Mock private lateinit var shadeViewController: ShadeViewController
@@ -144,6 +146,12 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
controller = createAndInitController(view)
}
+ `when`(
+ statusBarContentInsetsProviderForSecondaryDisplay
+ .getStatusBarContentInsetsForCurrentRotation()
+ )
+ .thenReturn(Insets.NONE)
+
val contextForSecondaryDisplay =
SysuiTestableContext(
mContext.createDisplayContext(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
index 5e870b19681b..163625747d85 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
@@ -25,6 +25,7 @@ import kotlinx.coroutines.flow.map
/** Fake implementation of [CommunalPrefsRepository] */
class FakeCommunalPrefsRepository : CommunalPrefsRepository {
private val _isCtaDismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
+ private val _isHubOnboardingismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
override fun isCtaDismissed(user: UserInfo): Flow<Boolean> =
_isCtaDismissed.map { it.contains(user) }
@@ -32,4 +33,12 @@ class FakeCommunalPrefsRepository : CommunalPrefsRepository {
override suspend fun setCtaDismissed(user: UserInfo) {
_isCtaDismissed.value = _isCtaDismissed.value.toMutableSet().apply { add(user) }
}
+
+ override fun isHubOnboardingDismissed(user: UserInfo): Flow<Boolean> =
+ _isHubOnboardingismissed.map { it.contains(user) }
+
+ override suspend fun setHubOnboardingDismissed(user: UserInfo) {
+ _isHubOnboardingismissed.value =
+ _isHubOnboardingismissed.value.toMutableSet().apply { add(user) }
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/HubOnboardingInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/HubOnboardingInteractorKosmos.kt
new file mode 100644
index 000000000000..9db4e4f4b8f2
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/HubOnboardingInteractorKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.domain.interactor
+
+import com.android.systemui.communal.data.repository.communalSettingsRepository
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.hubOnboardingInteractor: HubOnboardingInteractor by
+ Kosmos.Fixture {
+ HubOnboardingInteractor(
+ communalSceneInteractor = communalSceneInteractor,
+ communalSettingsRepository = communalSettingsRepository,
+ communalPrefsInteractor = communalPrefsInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt
new file mode 100644
index 000000000000..8a597a61ee78
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.posturing.data.repository
+
+import com.android.systemui.communal.posturing.shared.model.PosturedState
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakePosturingRepository : PosturingRepository {
+ private val _postured = MutableStateFlow<PosturedState>(PosturedState.Unknown)
+
+ override val posturedState: StateFlow<PosturedState> = _postured.asStateFlow()
+
+ fun setPosturedState(state: PosturedState) {
+ _postured.value = state
+ }
+}
+
+val PosturingRepository.fake
+ get() = this as FakePosturingRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeUserActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/PosturingRepositoryKosmos.kt
index 6345c4076412..105a3581b787 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeUserActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/PosturingRepositoryKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,11 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.shade.ui.viewmodel
+package com.android.systemui.communal.posturing.data.repository
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeUserActionsViewModel
-val Kosmos.notificationsShadeUserActionsViewModel:
- NotificationsShadeUserActionsViewModel by Fixture { NotificationsShadeUserActionsViewModel() }
+val Kosmos.posturingRepository by Kosmos.Fixture<PosturingRepository> { FakePosturingRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt
new file mode 100644
index 000000000000..53c9c6440c69
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.posturing.domain.interactor
+
+import com.android.systemui.communal.posturing.data.repository.posturingRepository
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.posturingInteractor by
+ Kosmos.Fixture<PosturingInteractor> { PosturingInteractor(repository = posturingRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/HubOnboardingViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/HubOnboardingViewModelKosmos.kt
new file mode 100644
index 000000000000..9cdaaf4b88f8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/HubOnboardingViewModelKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.communal.ui.viewmodel
+
+import com.android.systemui.communal.domain.interactor.hubOnboardingInteractor
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.hubOnboardingViewModel by
+ Kosmos.Fixture { HubOnboardingViewModel(hubOnboardingInteractor = hubOnboardingInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeFocusedDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeFocusedDisplayRepository.kt
index 83df5d874ad6..ad9370f7ac84 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeFocusedDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeFocusedDisplayRepository.kt
@@ -33,7 +33,7 @@ class FakeFocusedDisplayRepository @Inject constructor() : FocusedDisplayReposit
override val focusedDisplayId: StateFlow<Int>
get() = flow.asStateFlow()
- suspend fun emit(focusedDisplay: Int) = flow.emit(focusedDisplay)
+ suspend fun setDisplayId(focusedDisplay: Int) = flow.emit(focusedDisplay)
}
@Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigKosmos.kt
deleted file mode 100644
index 568324832b33..000000000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfigKosmos.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.keyguard.data.quickaffordance
-
-import android.content.applicationContext
-import com.android.systemui.communal.data.repository.communalSceneRepository
-import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-
-val Kosmos.glanceableHubQuickAffordanceConfig by
- Kosmos.Fixture {
- GlanceableHubQuickAffordanceConfig(
- context = applicationContext,
- communalInteractor = communalInteractor,
- communalSceneRepository = communalSceneRepository,
- communalSettingsInteractor = communalSettingsInteractor,
- sceneInteractor = sceneInteractor,
- )
- }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelFactoryKosmos.kt
new file mode 100644
index 000000000000..16d3fdc26613
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardMediaViewModelFactoryKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
+
+val Kosmos.keyguardMediaViewModelFactory by
+ Kosmos.Fixture {
+ object : KeyguardMediaViewModel.Factory {
+ override fun create(): KeyguardMediaViewModel {
+ return KeyguardMediaViewModel(mediaCarouselInteractor, keyguardInteractor)
+ }
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
index aa6ce9a433df..f8fa5db4ddf7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
@@ -23,6 +23,7 @@ import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.quickQuickSettingsViewModelFactory
import com.android.systemui.qs.panels.ui.viewmodel.tileGridViewModel
import com.android.systemui.qs.panels.ui.viewmodel.toolbar.toolbarViewModelFactory
+import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory
val Kosmos.quickSettingsContainerViewModelFactory by
Kosmos.Fixture {
@@ -33,6 +34,7 @@ val Kosmos.quickSettingsContainerViewModelFactory by
return QuickSettingsContainerViewModel(
brightnessSliderViewModelFactory,
quickQuickSettingsViewModelFactory,
+ shadeHeaderViewModelFactory,
supportsBrightnessMirroring,
tileGridViewModel,
editModeViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
index aaef27d257c5..d9a348d93533 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
@@ -16,12 +16,15 @@
package com.android.systemui.shade.data.repository
+import com.android.systemui.display.data.repository.FakeFocusedDisplayRepository
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.shade.display.AnyExternalShadeDisplayPolicy
import com.android.systemui.shade.display.DefaultDisplayShadePolicy
+import com.android.systemui.shade.display.FakeShadeDisplayPolicy
+import com.android.systemui.shade.display.FocusShadeDisplayPolicy
import com.android.systemui.shade.display.ShadeDisplayPolicy
import com.android.systemui.shade.display.ShadeExpansionIntent
import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
@@ -46,8 +49,6 @@ val Kosmos.statusBarTouchShadeDisplayPolicy: StatusBarTouchShadeDisplayPolicy by
StatusBarTouchShadeDisplayPolicy(
displayRepository = displayRepository,
backgroundScope = testScope.backgroundScope,
- keyguardRepository = keyguardRepository,
- shadeOnDefaultDisplayWhenLocked = false,
shadeInteractor = { shadeInteractor },
notificationElement = { notificationElement },
qsShadeElement = { qsElement },
@@ -55,13 +56,15 @@ val Kosmos.statusBarTouchShadeDisplayPolicy: StatusBarTouchShadeDisplayPolicy by
}
val Kosmos.shadeExpansionIntent: ShadeExpansionIntent by
Kosmos.Fixture { statusBarTouchShadeDisplayPolicy }
-val Kosmos.shadeDisplaysRepository: MutableShadeDisplaysRepository by
+val Kosmos.shadeDisplaysRepository: ShadeDisplaysRepository by
Kosmos.Fixture {
ShadeDisplaysRepositoryImpl(
bgScope = testScope.backgroundScope,
globalSettings = fakeGlobalSettings,
policies = shadeDisplayPolicies,
defaultPolicy = defaultShadeDisplayPolicy,
+ shadeOnDefaultDisplayWhenLocked = true,
+ keyguardRepository = keyguardRepository,
)
}
@@ -71,8 +74,16 @@ val Kosmos.shadeDisplayPolicies: Set<ShadeDisplayPolicy> by
defaultShadeDisplayPolicy,
anyExternalShadeDisplayPolicy,
statusBarTouchShadeDisplayPolicy,
+ FakeShadeDisplayPolicy,
)
}
val Kosmos.fakeShadeDisplaysRepository: FakeShadeDisplayRepository by
Kosmos.Fixture { FakeShadeDisplayRepository() }
+val Kosmos.fakeFocusedDisplayRepository: FakeFocusedDisplayRepository by
+ Kosmos.Fixture { FakeFocusedDisplayRepository() }
+
+val Kosmos.focusShadeDisplayPolicy: FocusShadeDisplayPolicy by
+ Kosmos.Fixture {
+ FocusShadeDisplayPolicy(focusedDisplayRepository = fakeFocusedDisplayRepository)
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt
index 923de2dcbf68..170d067b5c0c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt
@@ -18,12 +18,16 @@ package com.android.systemui.shade.domain.interactor
import android.content.mockedContext
import android.window.WindowContext
+import com.android.systemui.common.ui.data.repository.configurationRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.shade.ShadeDisplayChangeLatencyTracker
import com.android.systemui.shade.ShadeWindowLayoutParams
import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
import com.android.systemui.shade.data.repository.shadeExpansionIntent
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.statusbar.notification.row.notificationRebindingTracker
+import com.android.systemui.statusbar.notification.stack.notificationStackRebindingHider
import java.util.Optional
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
@@ -46,10 +50,14 @@ val Kosmos.shadeDisplaysInteractor by
ShadeDisplaysInteractor(
fakeShadeDisplaysRepository,
mockedWindowContext,
+ configurationRepository,
testScope.backgroundScope,
testScope.backgroundScope.coroutineContext,
mockedShadeDisplayChangeLatencyTracker,
Optional.of(shadeExpandedStateInteractor),
shadeExpansionIntent,
+ activeNotificationsInteractor,
+ notificationRebindingTracker,
+ Optional.of(notificationStackRebindingHider),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
index 7892e962d63d..1b50094ec0b7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
@@ -16,14 +16,57 @@
package com.android.systemui.shade.domain.interactor
+import android.provider.Settings
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shared.settings.data.repository.secureSettingsRepository
+import kotlinx.coroutines.launch
val Kosmos.shadeModeInteractor by Fixture {
ShadeModeInteractorImpl(
applicationScope = applicationCoroutineScope,
repository = shadeRepository,
+ secureSettingsRepository = secureSettingsRepository,
)
}
+
+// TODO(b/391578667): Make this user-aware once supported by FakeSecureSettingsRepository.
+/**
+ * Enables the Dual Shade setting, and (optionally) sets the shade layout to be wide (`true`)
+ * or narrow (`false`).
+ *
+ * In a wide layout, notifications and quick settings shades each take up only half the screen
+ * width. In a narrow layout, they each take up the entire screen width.
+ */
+fun Kosmos.enableDualShade(wideLayout: Boolean? = null) {
+ testScope.launch {
+ secureSettingsRepository.setInt(Settings.Secure.DUAL_SHADE, 1)
+
+ if (wideLayout != null) {
+ fakeShadeRepository.setShadeLayoutWide(wideLayout)
+ }
+ }
+}
+
+// TODO(b/391578667): Make this user-aware once supported by FakeSecureSettingsRepository.
+fun Kosmos.disableDualShade() {
+ testScope.launch { secureSettingsRepository.setInt(Settings.Secure.DUAL_SHADE, 0) }
+}
+
+fun Kosmos.enableSingleShade() {
+ testScope.launch {
+ disableDualShade()
+ fakeShadeRepository.setShadeLayoutWide(false)
+ }
+}
+
+fun Kosmos.enableSplitShade() {
+ testScope.launch {
+ disableDualShade()
+ fakeShadeRepository.setShadeLayoutWide(true)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
index f5b856df8835..7eb9f3472482 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
@@ -20,6 +20,7 @@ import android.content.applicationContext
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.plugins.activityStarter
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.privacyChipInteractor
import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -31,6 +32,7 @@ val Kosmos.shadeHeaderViewModel: ShadeHeaderViewModel by
ShadeHeaderViewModel(
context = applicationContext,
activityStarter = activityStarter,
+ sceneInteractor = sceneInteractor,
shadeInteractor = shadeInteractor,
mobileIconsInteractor = mobileIconsInteractor,
mobileIconsViewModel = mobileIconsViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index 3fddd47c25f0..6246d986f9e5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -380,6 +380,7 @@ class ExpandableNotificationRowBuilder(
mSmartReplyController,
Mockito.mock(IStatusBarService::class.java, STUB_ONLY),
Mockito.mock(UiEventLogger::class.java, STUB_ONLY),
+ Mockito.mock(NotificationRebindingTracker::class.java, STUB_ONLY),
)
row.setAboveShelfChangedListener {}
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/NotificationRebindingTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/NotificationRebindingTrackerKosmos.kt
new file mode 100644
index 000000000000..deac37a55717
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/NotificationRebindingTrackerKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.row
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+
+val Kosmos.notificationRebindingTracker by
+ Kosmos.Fixture {
+ NotificationRebindingTracker(
+ activeNotificationsInteractor = activeNotificationsInteractor,
+ bgScope = testScope.backgroundScope,
+ appScope = testScope.backgroundScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerKosmos.kt
index 569429f180df..73f296415a5a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerKosmos.kt
@@ -21,3 +21,6 @@ import com.android.systemui.util.mockito.mock
val Kosmos.notificationStackScrollLayoutController by
Kosmos.Fixture { mock<NotificationStackScrollLayoutController>() }
+
+val Kosmos.notificationStackRebindingHider by
+ Kosmos.Fixture { mock<NotificationStackRebindingHider>() }
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 8e998426685b..65550f2b4273 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -175,9 +175,9 @@ java_library {
}
java_device_for_host {
- name: "ravenwood-junit-impl-for-ravenizer",
+ name: "ravenwood-junit-for-ravenizer",
libs: [
- "ravenwood-junit-impl",
+ "ravenwood-junit",
],
visibility: [":__subpackages__"],
}
@@ -661,6 +661,9 @@ android_ravenwood_libgroup {
// StatsD
"framework-statsd.ravenwood",
+ // Graphics
+ "framework-graphics.ravenwood",
+
// Provide runtime versions of utils linked in below
"junit",
"truth",
diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp
index 99fc31b258e9..71496b0d5766 100644
--- a/ravenwood/Framework.bp
+++ b/ravenwood/Framework.bp
@@ -399,3 +399,55 @@ java_genrule {
"framework-statsd.ravenwood.jar",
],
}
+
+//////////////////////
+// framework-graphics
+//////////////////////
+
+java_genrule {
+ name: "framework-graphics.ravenwood-base",
+ tools: ["hoststubgen"],
+ cmd: "$(location hoststubgen) " +
+ "@$(location :ravenwood-standard-options) " +
+
+ "--debug-log $(location framework-graphics.log) " +
+ "--stats-file $(location framework-graphics_stats.csv) " +
+ "--supported-api-list-file $(location framework-graphics_apis.csv) " +
+ "--gen-keep-all-file $(location framework-graphics_keep_all.txt) " +
+ "--gen-input-dump-file $(location framework-graphics_dump.txt) " +
+
+ "--out-impl-jar $(location ravenwood.jar) " +
+ "--in-jar $(location :framework-graphics.impl{.jar}) " +
+
+ "--policy-override-file $(location :ravenwood-common-policies) ",
+ srcs: [
+ ":framework-graphics.impl{.jar}",
+
+ ":ravenwood-common-policies",
+ ":ravenwood-standard-options",
+ ],
+ out: [
+ "ravenwood.jar",
+
+ // Following files are created just as FYI.
+ "framework-graphics_keep_all.txt",
+ "framework-graphics_dump.txt",
+
+ "framework-graphics.log",
+ "framework-graphics_stats.csv",
+ "framework-graphics_apis.csv",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+java_genrule {
+ name: "framework-graphics.ravenwood",
+ defaults: ["ravenwood-internal-only-visibility-genrule"],
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":framework-graphics.ravenwood-base{ravenwood.jar}",
+ ],
+ out: [
+ "framework-graphics.ravenwood.jar",
+ ],
+}
diff --git a/ravenwood/tools/ravenizer/Android.bp b/ravenwood/tools/ravenizer/Android.bp
index 2892d0778ec6..a52a04b44f2d 100644
--- a/ravenwood/tools/ravenizer/Android.bp
+++ b/ravenwood/tools/ravenizer/Android.bp
@@ -19,7 +19,7 @@ java_binary_host {
"ow2-asm-tree",
"ow2-asm-util",
"junit",
- "ravenwood-junit-impl-for-ravenizer",
+ "ravenwood-junit-for-ravenizer",
],
visibility: ["//visibility:public"],
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index 8b758d29a2ac..3415300ec8d1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -16,7 +16,10 @@
package com.android.server.accessibility;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.MotionEvent.BUTTON_PRIMARY;
+import static android.view.accessibility.AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
+import static android.view.accessibility.AccessibilityManager.AUTOCLICK_DELAY_DEFAULT;
+import static android.view.accessibility.AccessibilityManager.AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT_DEFAULT;
import static com.android.server.accessibility.AutoclickIndicatorView.SHOW_INDICATOR_DELAY_TIME;
@@ -26,7 +29,6 @@ import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
-import android.graphics.PixelFormat;
import android.net.Uri;
import android.os.Handler;
import android.os.SystemClock;
@@ -37,10 +39,11 @@ import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.accessibility.util.AccessibilityUtils;
+
/**
* Implements "Automatically click on mouse stop" feature.
*
@@ -96,8 +99,7 @@ public class AutoclickController extends BaseEventStreamTransformation {
initiateAutoclickIndicator(handler);
}
- mClickScheduler =
- new ClickScheduler(handler, AccessibilityManager.AUTOCLICK_DELAY_DEFAULT);
+ mClickScheduler = new ClickScheduler(handler, AUTOCLICK_DELAY_DEFAULT);
mAutoclickSettingsObserver = new AutoclickSettingsObserver(mUserId, handler);
mAutoclickSettingsObserver.start(
mContext.getContentResolver(),
@@ -117,21 +119,8 @@ public class AutoclickController extends BaseEventStreamTransformation {
mAutoclickIndicatorScheduler = new AutoclickIndicatorScheduler(handler);
mAutoclickIndicatorView = new AutoclickIndicatorView(mContext);
- final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
- layoutParams.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
- layoutParams.flags =
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
- layoutParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- layoutParams.setFitInsetsTypes(0);
- layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- layoutParams.format = PixelFormat.TRANSLUCENT;
- layoutParams.setTitle(AutoclickIndicatorView.class.getSimpleName());
- layoutParams.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
-
mWindowManager = mContext.getSystemService(WindowManager.class);
- mWindowManager.addView(mAutoclickIndicatorView, layoutParams);
+ mWindowManager.addView(mAutoclickIndicatorView, mAutoclickIndicatorView.getLayoutParams());
}
@Override
@@ -209,6 +198,11 @@ public class AutoclickController extends BaseEventStreamTransformation {
private final Uri mAutoclickCursorAreaSizeSettingUri =
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE);
+ /** URI used to identify ignore minor cursor movement setting with content resolver. */
+ private final Uri mAutoclickIgnoreMinorCursorMovementSettingUri =
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT);
+
private ContentResolver mContentResolver;
private ClickScheduler mClickScheduler;
private AutoclickIndicatorScheduler mAutoclickIndicatorScheduler;
@@ -247,18 +241,32 @@ public class AutoclickController extends BaseEventStreamTransformation {
mContentResolver = contentResolver;
mClickScheduler = clickScheduler;
mAutoclickIndicatorScheduler = autoclickIndicatorScheduler;
- mContentResolver.registerContentObserver(mAutoclickDelaySettingUri, false, this,
+ mContentResolver.registerContentObserver(
+ mAutoclickDelaySettingUri,
+ /* notifyForDescendants= */ false,
+ /* observer= */ this,
mUserId);
// Initialize mClickScheduler's initial delay value.
- onChange(true, mAutoclickDelaySettingUri);
+ onChange(/* selfChange= */ true, mAutoclickDelaySettingUri);
if (Flags.enableAutoclickIndicator()) {
// Register observer to listen to cursor area size setting change.
mContentResolver.registerContentObserver(
- mAutoclickCursorAreaSizeSettingUri, false, this, mUserId);
+ mAutoclickCursorAreaSizeSettingUri,
+ /* notifyForDescendants= */ false,
+ /* observer= */ this,
+ mUserId);
// Initialize mAutoclickIndicatorView's initial size.
- onChange(true, mAutoclickCursorAreaSizeSettingUri);
+ onChange(/* selfChange= */ true, mAutoclickCursorAreaSizeSettingUri);
+
+ // Register observer to listen to ignore minor cursor movement setting change.
+ mContentResolver.registerContentObserver(
+ mAutoclickIgnoreMinorCursorMovementSettingUri,
+ /* notifyForDescendants= */ false,
+ /* observer= */ this,
+ mUserId);
+ onChange(/* selfChange= */ true, mAutoclickIgnoreMinorCursorMovementSettingUri);
}
}
@@ -279,21 +287,41 @@ public class AutoclickController extends BaseEventStreamTransformation {
@Override
public void onChange(boolean selfChange, Uri uri) {
if (mAutoclickDelaySettingUri.equals(uri)) {
- int delay = Settings.Secure.getIntForUser(
- mContentResolver, Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
- AccessibilityManager.AUTOCLICK_DELAY_DEFAULT, mUserId);
- mClickScheduler.updateDelay(delay);
- }
- if (Flags.enableAutoclickIndicator()
- && mAutoclickCursorAreaSizeSettingUri.equals(uri)) {
- int size =
+ int delay =
Settings.Secure.getIntForUser(
mContentResolver,
- Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE,
- AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT,
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
+ AUTOCLICK_DELAY_DEFAULT,
mUserId);
- if (mAutoclickIndicatorScheduler != null) {
- mAutoclickIndicatorScheduler.updateCursorAreaSize(size);
+ mClickScheduler.updateDelay(delay);
+ }
+
+ if (Flags.enableAutoclickIndicator()) {
+ if (mAutoclickCursorAreaSizeSettingUri.equals(uri)) {
+ int size =
+ Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE,
+ AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT,
+ mUserId);
+ if (mAutoclickIndicatorScheduler != null) {
+ mAutoclickIndicatorScheduler.updateCursorAreaSize(size);
+ }
+ mClickScheduler.updateMovementSlope(size);
+ }
+
+ if (mAutoclickIgnoreMinorCursorMovementSettingUri.equals(uri)) {
+ boolean ignoreMinorCursorMovement =
+ Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure
+ .ACCESSIBILITY_AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT,
+ AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT_DEFAULT
+ ? AccessibilityUtils.State.ON
+ : AccessibilityUtils.State.OFF,
+ mUserId)
+ == AccessibilityUtils.State.ON;
+ mClickScheduler.setIgnoreMinorCursorMovement(ignoreMinorCursorMovement);
}
}
}
@@ -365,11 +393,16 @@ public class AutoclickController extends BaseEventStreamTransformation {
@VisibleForTesting
final class ClickScheduler implements Runnable {
/**
- * Minimal distance pointer has to move relative to anchor in order for movement not to be
- * discarded as noise. Anchor is the position of the last MOVE event that was not considered
- * noise.
+ * Default minimal distance pointer has to move relative to anchor in order for movement not
+ * to be discarded as noise. Anchor is the position of the last MOVE event that was not
+ * considered noise.
*/
- private static final double MOVEMENT_SLOPE = 20f;
+ private static final double DEFAULT_MOVEMENT_SLOPE = 20f;
+
+ private double mMovementSlope = DEFAULT_MOVEMENT_SLOPE;
+
+ /** Whether the minor cursor movement should be ignored. */
+ private boolean mIgnoreMinorCursorMovement = AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT_DEFAULT;
/** Whether there is pending click. */
private boolean mActive;
@@ -553,7 +586,19 @@ public class AutoclickController extends BaseEventStreamTransformation {
float deltaX = mAnchorCoords.x - event.getX(pointerIndex);
float deltaY = mAnchorCoords.y - event.getY(pointerIndex);
double delta = Math.hypot(deltaX, deltaY);
- return delta > MOVEMENT_SLOPE;
+ double slope =
+ ((Flags.enableAutoclickIndicator() && mIgnoreMinorCursorMovement)
+ ? mMovementSlope
+ : DEFAULT_MOVEMENT_SLOPE);
+ return delta > slope;
+ }
+
+ public void setIgnoreMinorCursorMovement(boolean ignoreMinorCursorMovement) {
+ mIgnoreMinorCursorMovement = ignoreMinorCursorMovement;
+ }
+
+ private void updateMovementSlope(double slope) {
+ mMovementSlope = slope;
}
/**
@@ -581,18 +626,30 @@ public class AutoclickController extends BaseEventStreamTransformation {
final long now = SystemClock.uptimeMillis();
- MotionEvent downEvent = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1,
- mTempPointerProperties, mTempPointerCoords, mMetaState,
- MotionEvent.BUTTON_PRIMARY, 1.0f, 1.0f, mLastMotionEvent.getDeviceId(), 0,
- mLastMotionEvent.getSource(), mLastMotionEvent.getFlags());
+ MotionEvent downEvent =
+ MotionEvent.obtain(
+ /* downTime= */ now,
+ /* eventTime= */ now,
+ MotionEvent.ACTION_DOWN,
+ /* pointerCount= */ 1,
+ mTempPointerProperties,
+ mTempPointerCoords,
+ mMetaState,
+ BUTTON_PRIMARY,
+ /* xPrecision= */ 1.0f,
+ /* yPrecision= */ 1.0f,
+ mLastMotionEvent.getDeviceId(),
+ /* edgeFlags= */ 0,
+ mLastMotionEvent.getSource(),
+ mLastMotionEvent.getFlags());
MotionEvent pressEvent = MotionEvent.obtain(downEvent);
pressEvent.setAction(MotionEvent.ACTION_BUTTON_PRESS);
- pressEvent.setActionButton(MotionEvent.BUTTON_PRIMARY);
+ pressEvent.setActionButton(BUTTON_PRIMARY);
MotionEvent releaseEvent = MotionEvent.obtain(downEvent);
releaseEvent.setAction(MotionEvent.ACTION_BUTTON_RELEASE);
- releaseEvent.setActionButton(MotionEvent.BUTTON_PRIMARY);
+ releaseEvent.setActionButton(BUTTON_PRIMARY);
releaseEvent.setButtonState(0);
MotionEvent upEvent = MotionEvent.obtain(downEvent);
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java b/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
index f87dcdb200bb..93ce1f044f0b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
@@ -16,15 +16,19 @@
package com.android.server.accessibility;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.accessibility.AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.PixelFormat;
import android.graphics.RectF;
import android.util.DisplayMetrics;
import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.LinearInterpolator;
@@ -81,6 +85,26 @@ public class AutoclickIndicatorView extends View {
mRingRect = new RectF();
}
+ /**
+ * Retrieves the layout params for AutoclickIndicatorView, used when it's added to the Window
+ * Manager.
+ */
+ public final WindowManager.LayoutParams getLayoutParams() {
+ final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
+ layoutParams.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+ layoutParams.flags =
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+ layoutParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ layoutParams.setFitInsetsTypes(WindowInsets.Type.statusBars());
+ layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ layoutParams.format = PixelFormat.TRANSLUCENT;
+ layoutParams.setTitle(AutoclickIndicatorView.class.getSimpleName());
+ layoutParams.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
+ return layoutParams;
+ }
+
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index c68e54956c99..25494fce9fbf 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -19,6 +19,7 @@ package com.android.server.autofill;
import static android.Manifest.permission.MANAGE_AUTO_FILL;
import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
import static android.service.autofill.Flags.fixGetAutofillComponent;
+import static android.service.autofill.Flags.improveFillDialogAconfig;
import static android.view.autofill.AutofillManager.MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
@@ -70,6 +71,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.TimeUtils;
+import android.view.InsetsController;
import android.view.autofill.AutofillFeatureFlags;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
@@ -96,6 +98,7 @@ import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import com.android.server.infra.SecureSettingsServiceNameResolver;
+import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -243,6 +246,8 @@ public final class AutofillManagerService
private static final boolean DEFAULT_PCC_USE_FALLBACK = true;
+ private static final boolean DBG = false;
+
public AutofillManagerService(Context context) {
super(context,
new SecureSettingsServiceNameResolver(context, Settings.Secure.AUTOFILL_SERVICE),
@@ -301,8 +306,79 @@ public final class AutofillManagerService
mCredentialAutofillService = null;
Slog.w(TAG, "Invalid CredentialAutofillService");
}
+
+ if (improveFillDialogAconfig()) {
+ WindowManagerInternal windowManagerInternal = LocalServices.getService(
+ WindowManagerInternal.class);
+ WindowManagerInternal.ImeInsetsAnimationChangeListener
+ imeInsetsAnimationChangeListener =
+ new WindowManagerInternal.ImeInsetsAnimationChangeListener() {
+ @Override
+ public void onAnimationStart(
+ @InsetsController.AnimationType int animationType, int userId) {
+ if (DBG) {
+ Slog.e(TAG,
+ "onAnimationStart() notifyImeAnimationStart() "
+ + "animationType:"
+ + String.valueOf(animationType));
+ }
+ synchronized (mLock) {
+
+ // We are mostly interested in animations that show up the IME
+ if (animationType == InsetsController.ANIMATION_TYPE_HIDE) {
+ // IME is going away
+ mIsImeShowing = false;
+ }
+ if (animationType != InsetsController.ANIMATION_TYPE_SHOW) {
+ return;
+ }
+ mIsImeShowing = true;
+ mImeAnimatingWhileShowingUp = true;
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ service.notifyImeAnimationStart();
+ } else if (sVerbose) {
+ Slog.v(TAG,
+ "notifyImeAnimationStart(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(
+ @InsetsController.AnimationType int animationType, int userId) {
+ if (DBG) {
+ Slog.e(TAG,
+ "onAnimationEnd() notifyImeAnimationEnd() "
+ + "animationType:"
+ + String.valueOf(animationType));
+ }
+ // We are only interested in animations that show up the IME
+ if (animationType != InsetsController.ANIMATION_TYPE_SHOW) {
+ return;
+ }
+ mImeAnimatingWhileShowingUp = false;
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ service.notifyImeAnimationEnd();
+ } else if (sVerbose) {
+ Slog.v(TAG, "notifyImeAnimationEnd(): no service for "
+ + userId);
+ }
+ }
+ }
+ };
+ windowManagerInternal.setImeInsetsAnimationChangeListener(
+ imeInsetsAnimationChangeListener);
+ }
}
+ public boolean mImeAnimatingWhileShowingUp = false;
+ public boolean mIsImeShowing = false;
+
@Override // from AbstractMasterSystemService
protected String getServiceSettingsProperty() {
return Settings.Secure.AUTOFILL_SERVICE;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index b39b5b1a7660..eda62334ff39 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -209,6 +209,11 @@ final class AutofillManagerServiceImpl
private final DisabledInfoCache mDisabledInfoCache;
+ // Tracks active session id. There is no guarantee that such a session exists. For eg, if the
+ // session is destroyed, the id may no longer be valid. We don't update the state in all the
+ // cases.
+ private int mActiveSessionId = NO_SESSION;
+
AutofillManagerServiceImpl(AutofillManagerService master, Object lock,
LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
AutofillCompatState autofillCompatState,
@@ -387,6 +392,7 @@ final class AutofillManagerServiceImpl
@NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
@NonNull ComponentName clientActivity, boolean compatMode,
boolean bindInstantServiceAllowed, int flags) {
+ mActiveSessionId = NO_SESSION;
// FLAG_AUGMENTED_AUTOFILL_REQUEST is set in the flags when standard autofill is disabled
// but the package is allowlisted for augmented autofill
boolean forAugmentedAutofillOnly = (flags
@@ -445,6 +451,7 @@ final class AutofillManagerServiceImpl
if (newSession == null) {
return NO_SESSION;
}
+ mActiveSessionId = newSession.id;
// Service can be null when it's only for augmented autofill
String servicePackageName = mInfo == null ? null : mInfo.getServiceInfo().packageName;
@@ -748,6 +755,7 @@ final class AutofillManagerServiceImpl
Slog.d(TAG, "restarting session " + sessionId + " due to manual request on "
+ autofillId);
}
+ mActiveSessionId = sessionId;
return true;
}
if (sVerbose) {
@@ -757,6 +765,8 @@ final class AutofillManagerServiceImpl
return false;
}
+
+ mActiveSessionId = sessionId;
session.updateLocked(autofillId, virtualBounds, value, action, flags);
return false;
}
@@ -876,21 +886,54 @@ final class AutofillManagerServiceImpl
}
@GuardedBy("mLock")
+ public void notifyImeAnimationStart() {
+ if (!isEnabledLocked()) {
+ Slog.wtf(TAG, "Service not enabled");
+ return;
+ }
+ final Session session = mSessions.get(mActiveSessionId);
+ if (session == null) {
+ Slog.v(TAG, "notifyImeAnimationEnd(): no session for " + mActiveSessionId);
+ return;
+ }
+ session.notifyImeAnimationStart(SystemClock.elapsedRealtime());
+ }
+
+ @GuardedBy("mLock")
public void notifyImeAnimationEnd(int sessionId, long endTimeMs, int uid) {
if (!isEnabledLocked()) {
Slog.wtf(TAG, "Service not enabled");
return;
}
final Session session = mSessions.get(sessionId);
- if (session == null || uid != session.uid) {
+ if (session == null) {
Slog.v(TAG, "notifyImeAnimationEnd(): no session for "
+ sessionId + "(" + uid + ")");
return;
}
+ if (uid != session.uid) {
+ Slog.v(TAG, "notifyImeAnimationEnd(): Mismatched session id's "
+ + sessionId + "(" + uid + ")");
+ return;
+ }
session.notifyImeAnimationEnd(endTimeMs);
}
@GuardedBy("mLock")
+ public void notifyImeAnimationEnd() {
+ if (!isEnabledLocked()) {
+ Slog.wtf(TAG, "Service not enabled");
+ return;
+ }
+ final Session session = mSessions.get(mActiveSessionId);
+ if (session == null) {
+ Slog.v(TAG, "notifyImeAnimationEnd(): no session for " + mActiveSessionId);
+ return;
+ }
+ session.notifyImeAnimationEnd(SystemClock.elapsedRealtime());
+ }
+
+ @GuardedBy("mLock")
@Override // from PerUserSystemService
protected void handlePackageUpdateLocked(@NonNull String packageName) {
final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index a54a66312b1d..5184a2c4ec30 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -111,6 +111,7 @@ public class SettingsToPropertiesMapper {
DeviceConfig.NAMESPACE_LMKD_NATIVE,
DeviceConfig.NAMESPACE_MEDIA_NATIVE,
DeviceConfig.NAMESPACE_MGLRU_NATIVE,
+ DeviceConfig.NAMESPACE_MMD_NATIVE,
DeviceConfig.NAMESPACE_NETD_NATIVE,
DeviceConfig.NAMESPACE_NNAPI_NATIVE,
DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 45baf7bb0f09..709c13bc9704 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -13034,10 +13034,10 @@ public class AudioService extends IAudioService.Stub
int uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID);
if (intent.getBooleanExtra(EXTRA_REPLACING, false) ||
intent.getBooleanExtra(EXTRA_ARCHIVAL, false)) return;
- if (action.equals(ACTION_PACKAGE_ADDED)) {
+ if (ACTION_PACKAGE_ADDED.equals(action)) {
audioserverExecutor.execute(() ->
provider.onModifyPackageState(uid, pkgName, false /* isRemoved */));
- } else if (action.equals(ACTION_PACKAGE_REMOVED)) {
+ } else if (ACTION_PACKAGE_REMOVED.equals(action)) {
audioserverExecutor.execute(() ->
provider.onModifyPackageState(uid, pkgName, true /* isRemoved */));
}
diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java
index f66c7e115fc0..677e0c055455 100644
--- a/services/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -35,7 +35,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
-import com.android.server.backup.Flags;
+import com.android.server.display.DisplayBackupHelper;
import com.android.server.notification.NotificationBackupHelper;
import com.google.android.collect.Sets;
@@ -67,6 +67,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
private static final String APP_GENDER_HELPER = "app_gender";
private static final String COMPANION_HELPER = "companion";
private static final String SYSTEM_GENDER_HELPER = "system_gender";
+ private static final String DISPLAY_HELPER = "display";
// These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME
// are also used in the full-backup file format, so must not change unless steps are
@@ -104,7 +105,8 @@ public class SystemBackupAgent extends BackupAgentHelper {
APP_LOCALES_HELPER,
COMPANION_HELPER,
APP_GENDER_HELPER,
- SYSTEM_GENDER_HELPER);
+ SYSTEM_GENDER_HELPER,
+ DISPLAY_HELPER);
/** Helpers that are enabled for full, non-system users. */
private static final Set<String> sEligibleHelpersForNonSystemUser =
@@ -146,6 +148,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
addHelperIfEligibleForUser(COMPANION_HELPER, new CompanionBackupHelper(mUserId));
addHelperIfEligibleForUser(SYSTEM_GENDER_HELPER,
new SystemGrammaticalGenderBackupHelper(mUserId));
+ addHelperIfEligibleForUser(DISPLAY_HELPER, new DisplayBackupHelper(mUserId));
}
@Override
diff --git a/services/core/java/com/android/server/display/DisplayBackupHelper.java b/services/core/java/com/android/server/display/DisplayBackupHelper.java
new file mode 100644
index 000000000000..0d3a09fb2305
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayBackupHelper.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 android.annotation.Nullable;
+import android.app.backup.BlobBackupHelper;
+import android.hardware.display.DisplayManagerInternal;
+import android.util.AtomicFile;
+import android.util.AtomicFileOutputStream;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.display.utils.DebugUtils;
+
+import java.io.IOException;
+
+/**
+ * Display manager specific information backup helper. Backs-up the entire files for the given
+ * user.
+ * @hide
+ */
+public class DisplayBackupHelper extends BlobBackupHelper {
+ private static final String TAG = "DisplayBackupHelper";
+
+ // current schema of the backup state blob
+ private static final int BLOB_VERSION = 1;
+
+ // key under which the data blob is committed to back up
+ private static final String KEY_DISPLAY = "display";
+
+ // To enable these logs, run:
+ // adb shell setprop persist.log.tag.DisplayBackupHelper DEBUG
+ // adb reboot
+ private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
+
+ private final int mUserId;
+ private final Injector mInjector;
+
+ /**
+ * Construct a helper to manage backup/restore of entire files within Display Manager.
+ *
+ * @param userId id of the user for which backup will be done.
+ */
+ public DisplayBackupHelper(int userId) {
+ this(userId, new Injector());
+ }
+
+ @VisibleForTesting
+ DisplayBackupHelper(int userId, Injector injector) {
+ super(BLOB_VERSION, KEY_DISPLAY);
+ mUserId = userId;
+ mInjector = injector;
+ }
+
+ @Override
+ protected byte[] getBackupPayload(String key) {
+ if (!KEY_DISPLAY.equals(key) || !mInjector.isDisplayTopologyFlagEnabled()) {
+ return null;
+ }
+ try {
+ var result = mInjector.readTopologyFile(mUserId);
+ Slog.i(TAG, "getBackupPayload for " + key + " done, size=" + result.length);
+ return result;
+ } catch (IOException e) {
+ if (DEBUG) Slog.d(TAG, "Skip topology backup", e);
+ return null;
+ }
+ }
+
+ @Override
+ protected void applyRestoredPayload(String key, byte[] payload) {
+ if (!KEY_DISPLAY.equals(key) || !mInjector.isDisplayTopologyFlagEnabled()) {
+ return;
+ }
+ try (var oStream = mInjector.writeTopologyFile(mUserId)) {
+ oStream.write(payload);
+ oStream.markSuccess();
+ Slog.i(TAG, "applyRestoredPayload for " + key + " size=" + payload.length
+ + " to " + oStream);
+ } catch (IOException e) {
+ Slog.e(TAG, "applyRestoredPayload failed", e);
+ return;
+ }
+ var displayManagerInternal = mInjector.getDisplayManagerInternal();
+ if (displayManagerInternal == null) {
+ Slog.e(TAG, "DisplayManagerInternal is null");
+ return;
+ }
+
+ displayManagerInternal.reloadTopologies(mUserId);
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ private final boolean mIsDisplayTopologyEnabled =
+ new DisplayManagerFlags().isDisplayTopologyEnabled();
+
+ boolean isDisplayTopologyFlagEnabled() {
+ return mIsDisplayTopologyEnabled;
+ }
+
+ @Nullable
+ DisplayManagerInternal getDisplayManagerInternal() {
+ return LocalServices.getService(DisplayManagerInternal.class);
+ }
+
+ byte[] readTopologyFile(int userId) throws IOException {
+ return getTopologyFile(userId).readFully();
+ }
+
+ AtomicFileOutputStream writeTopologyFile(int userId) throws IOException {
+ return new AtomicFileOutputStream(getTopologyFile(userId));
+ }
+
+ private AtomicFile getTopologyFile(int userId) {
+ return new AtomicFile(DisplayTopologyXmlStore.getUserTopologyFile(userId),
+ /*commitTag=*/ "topology-state");
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 3598b9b2e879..49b451a83efa 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -37,10 +37,12 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_S
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
import static android.hardware.display.DisplayManagerGlobal.DisplayEvent;
import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
@@ -70,6 +72,7 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
+import android.app.backup.BackupManager;
import android.app.compat.CompatChanges;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.VirtualDeviceManager;
@@ -676,9 +679,10 @@ public final class DisplayManagerService extends SystemService {
mExternalDisplayStatsService);
mExternalDisplayPolicy = new ExternalDisplayPolicy(new ExternalDisplayPolicyInjector());
if (mFlags.isDisplayTopologyEnabled()) {
- mDisplayTopologyCoordinator =
- new DisplayTopologyCoordinator(this::isExtendedDisplayEnabled,
- this::deliverTopologyUpdate, new HandlerExecutor(mHandler), mSyncRoot);
+ final var backupManager = new BackupManager(mContext);
+ mDisplayTopologyCoordinator = new DisplayTopologyCoordinator(
+ this::isExtendedDisplayEnabled, this::deliverTopologyUpdate,
+ new HandlerExecutor(mHandler), mSyncRoot, backupManager::dataChanged);
} else {
mDisplayTopologyCoordinator = null;
}
@@ -758,6 +762,11 @@ public final class DisplayManagerService extends SystemService {
}
@Override
+ public void onUserUnlocked(@NonNull final TargetUser to) {
+ scheduleTopologiesReload(to.getUserIdentifier(), /*isUserSwitching=*/ true);
+ }
+
+ @Override
public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
final int newUserId = to.getUserIdentifier();
final int userSerial = getUserManager().getUserSerialNumber(newUserId);
@@ -796,6 +805,11 @@ public final class DisplayManagerService extends SystemService {
if (mFlags.isDisplayContentModeManagementEnabled()) {
updateMirrorBuiltInDisplaySettingLocked();
}
+
+ final UserManager userManager = getUserManager();
+ if (null != userManager && userManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
+ scheduleTopologiesReload(mCurrentUserId, /*isUserSwitching=*/ true);
+ }
}
}
@@ -873,6 +887,8 @@ public final class DisplayManagerService extends SystemService {
mSmallAreaDetectionController = (mFlags.isSmallAreaDetectionEnabled())
? SmallAreaDetectionController.create(mContext) : null;
+
+ scheduleTopologiesReload(mCurrentUserId, /*isUserSwitching=*/ false);
}
@VisibleForTesting
@@ -925,6 +941,15 @@ public final class DisplayManagerService extends SystemService {
return mDisplayNotificationManager;
}
+ private void scheduleTopologiesReload(final int userId, final boolean isUserSwitching) {
+ if (mDisplayTopologyCoordinator != null) {
+ // Need background thread due to xml files read operations not allowed on Display thread
+ BackgroundThread.getHandler().post(() ->
+ mDisplayTopologyCoordinator.reloadTopologies(
+ userId, isUserSwitching));
+ }
+ }
+
private void loadStableDisplayValuesLocked() {
final Point size = mPersistentDataStore.getStableDisplaySize();
if (size.x > 0 && size.y > 0) {
@@ -1800,7 +1825,11 @@ public final class DisplayManagerService extends SystemService {
}
if ((flags & VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
- flags |= VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+ if ((flags & VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) == 0) {
+ Slog.d(TAG, "Public virtual displays are auto mirror by default, hence adding "
+ + "VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR.");
+ flags |= VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+ }
// Public displays can't be allowed to show content when locked.
if ((flags & VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
@@ -1808,10 +1837,16 @@ public final class DisplayManagerService extends SystemService {
"Public display must not be marked as SHOW_WHEN_LOCKED_INSECURE");
}
}
- if ((flags & VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) != 0) {
+ if ((flags & VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) != 0
+ && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
+ Slog.d(TAG, "Own content displays cannot auto mirror other displays, hence ignoring "
+ + "VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR.");
flags &= ~VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
}
- if ((flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
+ if ((flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0
+ && (flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) {
+ Slog.d(TAG, "Auto mirror displays must be in the default display group, hence ignoring "
+ + "VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP.");
flags &= ~VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
}
// Put the display in the virtual device's display group only if it's not a mirror display,
@@ -1821,6 +1856,8 @@ public final class DisplayManagerService extends SystemService {
&& (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) == 0
&& (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == VIRTUAL_DISPLAY_FLAG_TRUSTED
&& virtualDevice != null) {
+ Slog.d(TAG, "Own content displays owned by virtual devices are put in that device's "
+ + "display group, hence adding VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP.");
flags |= VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP;
}
@@ -1852,8 +1889,7 @@ public final class DisplayManagerService extends SystemService {
Binder.restoreCallingIdentity(firstToken);
}
- if (callingUid != Process.SYSTEM_UID
- && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
+ if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
// Only a valid media projection or a virtual device can create a mirror virtual
// display.
if (!canProjectVideo(projection) && !canCreateMirrorDisplays(virtualDevice)
@@ -1901,6 +1937,14 @@ public final class DisplayManagerService extends SystemService {
}
}
+ if ((flags & VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED) != 0
+ && (flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) == 0
+ && (flags & VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP) == 0) {
+ Slog.d(TAG, "Always unlocked displays cannot be in the default display group, hence "
+ + "ignoring flag VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED.");
+ flags &= ~VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
+ }
+
if ((flags & VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED) != 0) {
if (callingUid != Process.SYSTEM_UID
&& !checkCallingPermission(ADD_ALWAYS_UNLOCKED_DISPLAY,
@@ -1911,7 +1955,24 @@ public final class DisplayManagerService extends SystemService {
}
}
- if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
+ if ((flags & VIRTUAL_DISPLAY_FLAG_OWN_FOCUS) != 0
+ && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
+ Slog.d(TAG, "Untrusted displays cannot have own focus, hence ignoring flag "
+ + "VIRTUAL_DISPLAY_FLAG_OWN_FOCUS.");
+ flags &= ~VIRTUAL_DISPLAY_FLAG_OWN_FOCUS;
+ }
+
+ if ((flags & VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED) != 0
+ && (flags & VIRTUAL_DISPLAY_FLAG_OWN_FOCUS) == 0) {
+ Slog.d(TAG, "Virtual displays that cannot steal top focus must have their own "
+ + " focus, hence ignoring flag VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED.");
+ flags &= ~VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED;
+ }
+
+ if ((flags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0
+ && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
+ Slog.d(TAG, "Untrusted displays cannot show system decorations, hence ignoring flag "
+ + "VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS.");
flags &= ~VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
}
@@ -4157,7 +4218,8 @@ public final class DisplayManagerService extends SystemService {
public boolean shouldReceiveRefreshRateWithChangeUpdate(int event) {
if (mFlags.isRefreshRateEventForForegroundAppsEnabled()
- && event == DisplayManagerGlobal.EVENT_DISPLAY_REFRESH_RATE_CHANGED) {
+ && event == DisplayManagerGlobal.EVENT_DISPLAY_REFRESH_RATE_CHANGED
+ && mActivityManagerInternal != null) {
int procState = mActivityManagerInternal.getUidProcessState(mUid);
int importance = ActivityManager.RunningAppProcessInfo
.procStateToImportance(procState);
@@ -5937,6 +5999,14 @@ public final class DisplayManagerService extends SystemService {
public boolean isDisplayReadyForMirroring(int displayId) {
return mExternalDisplayPolicy.isDisplayReadyForMirroring(displayId);
}
+
+ @Override
+ public void reloadTopologies(final int userId) {
+ // Reload topologies only if the userId matches the current user id.
+ if (userId == mCurrentUserId) {
+ scheduleTopologiesReload(mCurrentUserId, /*isUserSwitching=*/ false);
+ }
+ }
}
class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
index 88650394020b..c73ab32f117a 100644
--- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
@@ -19,6 +19,8 @@ package com.android.server.display;
import static android.hardware.display.DisplayTopology.pxToDp;
import android.hardware.display.DisplayTopology;
+import android.util.Slog;
+import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
@@ -26,6 +28,8 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
@@ -35,9 +39,25 @@ import java.util.function.Consumer;
* persisting the topology.
*/
class DisplayTopologyCoordinator {
+ private static final String TAG = "DisplayTopologyCoordinator";
+
+ private static String getUniqueId(DisplayInfo info) {
+ if (info.displayId == Display.DEFAULT_DISPLAY && info.type == Display.TYPE_INTERNAL) {
+ return "internal";
+ }
+ return info.uniqueId;
+ }
+
+ // Persistent data store for display topologies.
+ private final DisplayTopologyStore mTopologyStore;
@GuardedBy("mSyncRoot")
private DisplayTopology mTopology;
+ @GuardedBy("mSyncRoot")
+ private final Map<String, Integer> mUniqueIdToDisplayIdMapping = new HashMap<>();
+
+ @GuardedBy("mSyncRoot")
+ private final SparseArray<String> mDisplayIdToUniqueIdMapping = new SparseArray<>();
/**
* Check if extended displays are enabled. If not, a topology is not needed.
@@ -52,23 +72,29 @@ class DisplayTopologyCoordinator {
private final Consumer<DisplayTopology> mOnTopologyChangedCallback;
private final Executor mTopologyChangeExecutor;
private final DisplayManagerService.SyncRoot mSyncRoot;
+ private final Runnable mTopologySavedCallback;
DisplayTopologyCoordinator(BooleanSupplier isExtendedDisplayEnabled,
Consumer<DisplayTopology> onTopologyChangedCallback,
- Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot) {
+ Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot,
+ Runnable topologySavedCallback) {
this(new Injector(), isExtendedDisplayEnabled, onTopologyChangedCallback,
- topologyChangeExecutor, syncRoot);
+ topologyChangeExecutor, syncRoot, topologySavedCallback);
}
@VisibleForTesting
DisplayTopologyCoordinator(Injector injector, BooleanSupplier isExtendedDisplayEnabled,
Consumer<DisplayTopology> onTopologyChangedCallback,
- Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot) {
+ Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot,
+ Runnable topologySavedCallback) {
mTopology = injector.getTopology();
mIsExtendedDisplayEnabled = isExtendedDisplayEnabled;
mOnTopologyChangedCallback = onTopologyChangedCallback;
mTopologyChangeExecutor = topologyChangeExecutor;
mSyncRoot = syncRoot;
+ mTopologyStore = injector.createTopologyStore(
+ mDisplayIdToUniqueIdMapping, mUniqueIdToDisplayIdMapping);
+ mTopologySavedCallback = topologySavedCallback;
}
/**
@@ -80,7 +106,10 @@ class DisplayTopologyCoordinator {
return;
}
synchronized (mSyncRoot) {
+ addDisplayIdMappingLocked(info);
+
mTopology.addDisplay(info.displayId, getWidth(info), getHeight(info));
+ restoreTopologyLocked();
sendTopologyUpdateLocked();
}
}
@@ -104,12 +133,40 @@ class DisplayTopologyCoordinator {
void onDisplayRemoved(int displayId) {
synchronized (mSyncRoot) {
if (mTopology.removeDisplay(displayId)) {
+ removeDisplayIdMappingLocked(displayId);
+ restoreTopologyLocked();
sendTopologyUpdateLocked();
}
}
}
/**
+ * Loads all topologies from the persistent topology store for the given userId.
+ * @param userId the user id, same as returned from
+ * {@link android.app.ActivityManagerInternal#getCurrentUserId()}.
+ * @param isUserSwitching whether the id of the user is currently switching.
+ */
+ void reloadTopologies(int userId, boolean isUserSwitching) {
+ boolean isTopologySaved = false;
+ synchronized (mSyncRoot) {
+ mTopologyStore.reloadTopologies(userId);
+ boolean isTopologyRestored = restoreTopologyLocked();
+ if (isTopologyRestored) {
+ sendTopologyUpdateLocked();
+ }
+ if (isUserSwitching && !isTopologyRestored) {
+ // During user switch, if topology is not restored - last user topology is the
+ // good initial guess. Save this topology for consistent use in the future.
+ isTopologySaved = mTopologyStore.saveTopology(mTopology);
+ }
+ }
+
+ if (isTopologySaved) {
+ mTopologySavedCallback.run();
+ }
+ }
+
+ /**
* @return A deep copy of the topology.
*/
DisplayTopology getTopology() {
@@ -119,10 +176,16 @@ class DisplayTopologyCoordinator {
}
void setTopology(DisplayTopology topology) {
+ final boolean isTopologySaved;
synchronized (mSyncRoot) {
+ topology.normalize();
mTopology = topology;
- mTopology.normalize();
sendTopologyUpdateLocked();
+ isTopologySaved = mTopologyStore.saveTopology(topology);
+ }
+
+ if (isTopologySaved) {
+ mTopologySavedCallback.run();
}
}
@@ -136,6 +199,24 @@ class DisplayTopologyCoordinator {
}
}
+ @GuardedBy("mSyncRoot")
+ private void removeDisplayIdMappingLocked(final int displayId) {
+ final String uniqueId = mDisplayIdToUniqueIdMapping.get(displayId);
+ if (null == uniqueId) {
+ Slog.e(TAG, "Can't find uniqueId for displayId=" + displayId);
+ return;
+ }
+ mDisplayIdToUniqueIdMapping.remove(displayId);
+ mUniqueIdToDisplayIdMapping.remove(uniqueId);
+ }
+
+ @GuardedBy("mSyncRoot")
+ private void addDisplayIdMappingLocked(DisplayInfo info) {
+ final String uniqueId = getUniqueId(info);
+ mUniqueIdToDisplayIdMapping.put(uniqueId, info.displayId);
+ mDisplayIdToUniqueIdMapping.put(info.displayId, uniqueId);
+ }
+
/**
* @param info The display info
* @return The width of the display in dp
@@ -157,6 +238,21 @@ class DisplayTopologyCoordinator {
&& info.displayGroupId == Display.DEFAULT_DISPLAY_GROUP;
}
+ /**
+ * Restores {@link #mTopology} from {@link #mTopologyStore}, saves it in {@link #mTopology}.
+ * @return true if the topology was restored, false otherwise.
+ */
+ @GuardedBy("mSyncRoot")
+ private boolean restoreTopologyLocked() {
+ var restoredTopology = mTopologyStore.restoreTopology(mTopology);
+ if (restoredTopology == null) {
+ return false;
+ }
+ mTopology = restoredTopology;
+ mTopology.normalize();
+ return true;
+ }
+
@GuardedBy("mSyncRoot")
private void sendTopologyUpdateLocked() {
DisplayTopology copy = mTopology.copy();
@@ -168,5 +264,21 @@ class DisplayTopologyCoordinator {
DisplayTopology getTopology() {
return new DisplayTopology();
}
+
+ DisplayTopologyStore createTopologyStore(
+ SparseArray<String> displayIdToUniqueIdMapping,
+ Map<String, Integer> uniqueIdToDisplayIdMapping) {
+ return new DisplayTopologyXmlStore(new DisplayTopologyXmlStore.Injector() {
+ @Override
+ public SparseArray<String> getDisplayIdToUniqueIdMapping() {
+ return displayIdToUniqueIdMapping;
+ }
+
+ @Override
+ public Map<String, Integer> getUniqueIdToDisplayIdMapping() {
+ return uniqueIdToDisplayIdMapping;
+ }
+ });
+ }
}
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 558afd1da380..abbdeb9da364 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -33,13 +33,6 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPO
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TOUCH_FEEDBACK_DISABLED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
-import static com.android.server.display.DisplayDeviceInfo.FLAG_ALWAYS_UNLOCKED;
-import static com.android.server.display.DisplayDeviceInfo.FLAG_DEVICE_DISPLAY_GROUP;
-import static com.android.server.display.DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP;
-import static com.android.server.display.DisplayDeviceInfo.FLAG_STEAL_TOP_FOCUS_DISABLED;
-import static com.android.server.display.DisplayDeviceInfo.FLAG_TOUCH_FEEDBACK_DISABLED;
-import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED;
-
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Point;
@@ -574,15 +567,13 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mInfo.flags &= ~DisplayDeviceInfo.FLAG_NEVER_BLANK;
} else {
mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
-
- if ((mFlags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) {
- mInfo.flags |= FLAG_OWN_DISPLAY_GROUP;
- }
+ }
+ if ((mFlags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP;
}
if ((mFlags & VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP) != 0) {
- mInfo.flags |= FLAG_DEVICE_DISPLAY_GROUP;
+ mInfo.flags |= DisplayDeviceInfo.FLAG_DEVICE_DISPLAY_GROUP;
}
-
if ((mFlags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
}
@@ -611,41 +602,19 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mInfo.flags |= DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
}
if ((mFlags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
- mInfo.flags |= FLAG_TRUSTED;
+ mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED;
}
if ((mFlags & VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED) != 0) {
- if ((mInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0
- || (mFlags & VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP) != 0) {
- mInfo.flags |= FLAG_ALWAYS_UNLOCKED;
- } else {
- Slog.w(
- TAG,
- "Ignoring VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED as it requires"
- + " VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP or"
- + " VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP.");
- }
+ mInfo.flags |= DisplayDeviceInfo.FLAG_ALWAYS_UNLOCKED;
}
if ((mFlags & VIRTUAL_DISPLAY_FLAG_TOUCH_FEEDBACK_DISABLED) != 0) {
- mInfo.flags |= FLAG_TOUCH_FEEDBACK_DISABLED;
+ mInfo.flags |= DisplayDeviceInfo.FLAG_TOUCH_FEEDBACK_DISABLED;
}
if ((mFlags & VIRTUAL_DISPLAY_FLAG_OWN_FOCUS) != 0) {
- if ((mFlags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
- mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_FOCUS;
- } else {
- Slog.w(TAG, "Ignoring VIRTUAL_DISPLAY_FLAG_OWN_FOCUS as it requires "
- + "VIRTUAL_DISPLAY_FLAG_TRUSTED.");
- }
+ mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_FOCUS;
}
if ((mFlags & VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED) != 0) {
- if ((mFlags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0
- && (mFlags & VIRTUAL_DISPLAY_FLAG_OWN_FOCUS) != 0) {
- mInfo.flags |= FLAG_STEAL_TOP_FOCUS_DISABLED;
- } else {
- Slog.w(TAG,
- "Ignoring VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED as it "
- + "requires VIRTUAL_DISPLAY_FLAG_OWN_FOCUS which requires "
- + "VIRTUAL_DISPLAY_FLAG_TRUSTED.");
- }
+ mInfo.flags |= DisplayDeviceInfo.FLAG_STEAL_TOP_FOCUS_DISABLED;
}
mInfo.type = Display.TYPE_VIRTUAL;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index 550f68fbc94c..733448aa0ffc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -278,7 +278,8 @@ public class HdmiCecNetwork {
}
private void invokeDeviceEventListener(HdmiDeviceInfo info, int event) {
- if (!hideDevicesBehindLegacySwitch(info)) {
+ if (event == HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE ||
+ !hideDevicesBehindLegacySwitch(info)) {
mHdmiControlService.invokeDeviceEventListeners(info, event);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index bd8b67b9d626..89f0d0edbf2b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -710,7 +710,9 @@ public class HdmiControlService extends SystemService {
// Register ContentObserver to monitor the settings change.
registerContentObserver();
}
- mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED);
+ if (mMhlController != null) {
+ mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED);
+ }
}
@VisibleForTesting
@@ -1721,6 +1723,8 @@ public class HdmiControlService extends SystemService {
if (result != SendMessageResult.SUCCESS) {
localDevice.addAndStartAction(new
ResendCecCommandAction(localDevice, command, callback));
+ } else if (callback != null) {
+ callback.onSendCompleted(result);
}
}
});
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index 1e54beeb2d64..b9352edf9230 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -372,25 +372,8 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
/* package */ void onEndpointSessionOpenRequest(
int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
- if (!hasEndpointPermissions(initiator)) {
- halCloseEndpointSessionNoThrow(sessionId, Reason.PERMISSION_DENIED);
- return;
- }
-
- synchronized (mOpenSessionLock) {
- if (hasSessionId(sessionId)) {
- Log.e(TAG, "Existing session in onEndpointSessionOpenRequest: id=" + sessionId);
- halCloseEndpointSessionNoThrow(sessionId, Reason.UNSPECIFIED);
- return;
- }
- mSessionInfoMap.put(sessionId, new SessionInfo(initiator, true));
- }
-
boolean success =
- invokeCallback(
- (consumer) ->
- consumer.onSessionOpenRequest(
- sessionId, initiator, serviceDescriptor));
+ onEndpointSessionOpenRequestInternal(sessionId, initiator, serviceDescriptor);
if (!success) {
cleanupSessionResources(sessionId);
}
@@ -439,6 +422,33 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
}
}
+ private boolean onEndpointSessionOpenRequestInternal(
+ int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
+ if (!hasEndpointPermissions(initiator)) {
+ Log.e(
+ TAG,
+ "onEndpointSessionOpenRequest: "
+ + initiator
+ + " doesn't have permission for "
+ + mEndpointInfo);
+ halCloseEndpointSessionNoThrow(sessionId, Reason.PERMISSION_DENIED);
+ return false;
+ }
+
+ synchronized (mOpenSessionLock) {
+ if (hasSessionId(sessionId)) {
+ Log.e(TAG, "Existing session in onEndpointSessionOpenRequest: id=" + sessionId);
+ halCloseEndpointSessionNoThrow(sessionId, Reason.UNSPECIFIED);
+ return false;
+ }
+ mSessionInfoMap.put(sessionId, new SessionInfo(initiator, true));
+ }
+
+ return invokeCallback(
+ (consumer) ->
+ consumer.onSessionOpenRequest(sessionId, initiator, serviceDescriptor));
+ }
+
private byte onMessageReceivedInternal(int sessionId, HubMessage message) {
HubEndpointInfo remote;
synchronized (mOpenSessionLock) {
diff --git a/services/core/java/com/android/server/media/AudioManagerRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java
index 280d051be239..14f870bd4e87 100644
--- a/services/core/java/com/android/server/media/AudioManagerRouteController.java
+++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java
@@ -117,6 +117,35 @@ import java.util.concurrent.CopyOnWriteArrayList;
@NonNull
private MediaRoute2Info mSelectedRoute;
+ // A singleton AudioManagerRouteController.
+ private static AudioManagerRouteController mInstance;
+
+ // A flag indicating if the start function has been called.
+ private boolean mStarted = false;
+
+ // Get the singleton AudioManagerRouteController. Create a new one if it's not available yet.
+ public static AudioManagerRouteController getInstance(
+ @NonNull Context context,
+ @NonNull AudioManager audioManager,
+ @NonNull Looper looper,
+ @NonNull AudioProductStrategy strategyForMedia,
+ @NonNull BluetoothAdapter btAdapter) {
+ if (!com.android.media.flags.Flags.enableUseOfSingletonAudioManagerRouteController()) {
+ return new AudioManagerRouteController(
+ context, audioManager, looper, strategyForMedia, btAdapter);
+ }
+
+ synchronized (AudioManagerRouteController.class) {
+ if (mInstance == null) {
+ mInstance =
+ new AudioManagerRouteController(
+ context, audioManager, looper, strategyForMedia, btAdapter);
+ }
+
+ return mInstance;
+ }
+ }
+
// TODO: b/305199571 - Support nullable btAdapter and strategyForMedia which, when null, means
// no support for transferring to inactive bluetooth routes and transferring to any routes
// respectively.
@@ -130,13 +159,11 @@ import java.util.concurrent.CopyOnWriteArrayList;
@NonNull AudioManager audioManager,
@NonNull Looper looper,
@NonNull AudioProductStrategy strategyForMedia,
- @NonNull BluetoothAdapter btAdapter,
- @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
+ @NonNull BluetoothAdapter btAdapter) {
mContext = Objects.requireNonNull(context);
mAudioManager = Objects.requireNonNull(audioManager);
mHandler = new Handler(Objects.requireNonNull(looper));
mStrategyForMedia = Objects.requireNonNull(strategyForMedia);
- mOnDeviceRouteChangedListeners.add(Objects.requireNonNull(onDeviceRouteChangedListener));
mBuiltInSpeakerSuitabilityStatus =
DeviceRouteController.getBuiltInSpeakerSuitabilityStatus(mContext);
@@ -154,6 +181,11 @@ import java.util.concurrent.CopyOnWriteArrayList;
mOnDeviceRouteChangedListeners.add(onDeviceRouteChangedListener);
}
+ public void unregisterRouteChangeListener(
+ @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
+ mOnDeviceRouteChangedListeners.remove(onDeviceRouteChangedListener);
+ }
+
@RequiresPermission(
anyOf = {
Manifest.permission.MODIFY_AUDIO_ROUTING,
@@ -161,7 +193,18 @@ import java.util.concurrent.CopyOnWriteArrayList;
})
@Override
public void start(UserHandle mUser) {
- mBluetoothRouteController.start(mUser);
+ // When AudioManagerRouteController is singleton, only need to call this function once.
+ if (com.android.media.flags.Flags.enableUseOfSingletonAudioManagerRouteController()) {
+ if (mStarted) {
+ return;
+ }
+ mStarted = true;
+ }
+
+ mBluetoothRouteController.start(
+ com.android.media.flags.Flags.enableUseOfSingletonAudioManagerRouteController()
+ ? UserHandle.SYSTEM
+ : mUser);
mAudioManager.registerAudioDeviceCallback(mAudioDeviceCallback, mHandler);
mAudioManager.addOnDevicesForAttributesChangedListener(
AudioRoutingUtils.ATTRIBUTES_MEDIA,
@@ -176,6 +219,11 @@ import java.util.concurrent.CopyOnWriteArrayList;
})
@Override
public void stop() {
+ // Singleton AudioManagerRouteController doesn't need to call stop function.
+ if (com.android.media.flags.Flags.enableUseOfSingletonAudioManagerRouteController()) {
+ return;
+ }
+
mAudioManager.removeOnDevicesForAttributesChangedListener(
mOnDevicesForAttributesChangedListener);
mAudioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index dff0adfca370..0a4cbcd1ea31 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -65,13 +65,11 @@ import java.util.List;
if (strategyForMedia != null
&& btAdapter != null
&& Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
- return new AudioManagerRouteController(
- context,
- audioManager,
- looper,
- strategyForMedia,
- btAdapter,
- onDeviceRouteChangedListener);
+ AudioManagerRouteController controller =
+ AudioManagerRouteController.getInstance(
+ context, audioManager, looper, strategyForMedia, btAdapter);
+ controller.registerRouteChangeListener(onDeviceRouteChangedListener);
+ return controller;
} else {
IAudioService audioService =
IAudioService.Stub.asInterface(
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
index 011659a616d3..3eb38a7029e6 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
@@ -17,6 +17,7 @@
package com.android.server.media;
import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
+import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -217,6 +218,28 @@ import java.util.stream.Stream;
}
}
+ @Override
+ public void setRouteVolume(long requestId, String routeOriginalId, int volume) {
+ synchronized (mLock) {
+ var targetProviderProxyId = mOriginalRouteIdToProviderId.get(routeOriginalId);
+ var targetProviderProxyRecord = mProxyRecords.get(targetProviderProxyId);
+ // Holds the target route, if it's managed by a provider service. Holds null otherwise.
+ if (targetProviderProxyRecord != null) {
+ var serviceTargetRoute =
+ targetProviderProxyRecord.mNewOriginalIdToSourceOriginalIdMap.get(
+ routeOriginalId);
+ if (serviceTargetRoute != null) {
+ targetProviderProxyRecord.mProxy.setRouteVolume(
+ requestId, serviceTargetRoute, volume);
+ } else {
+ notifyRequestFailed(
+ requestId, MediaRoute2ProviderService.REASON_ROUTE_NOT_AVAILABLE);
+ }
+ }
+ }
+ super.setRouteVolume(requestId, routeOriginalId, volume);
+ }
+
/**
* Returns the uid that corresponds to the given name and user handle, or {@link
* Process#INVALID_UID} if a uid couldn't be found.
@@ -463,11 +486,18 @@ import java.util.stream.Stream;
}
String id =
asSystemRouteId(providerInfo.getUniqueId(), sourceRoute.getOriginalId());
- var newRoute =
- new MediaRoute2Info.Builder(id, sourceRoute.getName())
- .addFeature(FEATURE_LIVE_AUDIO)
- .build();
- routesMap.put(id, newRoute);
+ var newRouteBuilder = new MediaRoute2Info.Builder(id, sourceRoute);
+ if ((sourceRoute.getSupportedRoutingTypes()
+ & MediaRoute2Info.FLAG_ROUTING_TYPE_SYSTEM_AUDIO)
+ != 0) {
+ newRouteBuilder.addFeature(FEATURE_LIVE_AUDIO);
+ }
+ if ((sourceRoute.getSupportedRoutingTypes()
+ & MediaRoute2Info.FLAG_ROUTING_TYPE_SYSTEM_VIDEO)
+ != 0) {
+ newRouteBuilder.addFeature(FEATURE_LIVE_VIDEO);
+ }
+ routesMap.put(id, newRouteBuilder.build());
idMap.put(id, sourceRoute.getOriginalId());
}
return new ProviderProxyRecord(
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 c428f39fd9d0..34a6cb951d46 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -58,6 +58,7 @@ import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionCallback;
import android.media.projection.IMediaProjectionManager;
import android.media.projection.IMediaProjectionWatcherCallback;
+import android.media.projection.MediaProjectionEvent;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.media.projection.ReviewGrantedConsentResult;
@@ -80,6 +81,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.media.projection.flags.Flags;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.Watchdog;
@@ -177,9 +179,31 @@ public final class MediaProjectionManagerService extends SystemService
private void maybeStopMediaProjection(int reason) {
synchronized (mLock) {
- if (!mMediaProjectionStopController.isExemptFromStopping(mProjectionGrant, reason)) {
- Slog.d(TAG, "Content Recording: Stopping MediaProjection due to "
- + MediaProjectionStopController.stopReasonToString(reason));
+ if (mMediaProjectionStopController.isExemptFromStopping(mProjectionGrant, reason)) {
+ return;
+ }
+
+ if (Flags.showStopDialogPostCallEnd()
+ && mMediaProjectionStopController.isStopReasonCallEnd(reason)) {
+ MediaProjectionEvent event =
+ new MediaProjectionEvent(
+ MediaProjectionEvent
+ .PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL,
+ System.currentTimeMillis());
+ Slog.d(
+ TAG,
+ "Scheduling event: "
+ + event.getEventType()
+ + " for reason: "
+ + MediaProjectionStopController.stopReasonToString(reason));
+
+ // Post the PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL event with a delay.
+ mHandler.postDelayed(() -> dispatchEvent(event), 500);
+ } else {
+ Slog.d(
+ TAG,
+ "Stopping MediaProjection due to reason: "
+ + MediaProjectionStopController.stopReasonToString(reason));
mProjectionGrant.stop(StopReason.STOP_DEVICE_LOCKED);
}
}
@@ -388,6 +412,24 @@ public final class MediaProjectionManagerService extends SystemService
mCallbackDelegate.dispatchSession(projectionInfo, session);
}
+ private void dispatchEvent(@NonNull MediaProjectionEvent event) {
+ if (!Flags.showStopDialogPostCallEnd()) {
+ Slog.d(
+ TAG,
+ "Event dispatch skipped. Reason: Flag showStopDialogPostCallEnd "
+ + "is disabled. Event details: "
+ + event);
+ return;
+ }
+ MediaProjectionInfo projectionInfo;
+ ContentRecordingSession session;
+ synchronized (mLock) {
+ projectionInfo = mProjectionGrant != null ? mProjectionGrant.getProjectionInfo() : null;
+ session = mProjectionGrant != null ? mProjectionGrant.mSession : null;
+ }
+ mCallbackDelegate.dispatchEvent(event, projectionInfo, session);
+ }
+
/**
* Returns {@code true} when updating the current mirroring session on WM succeeded, and
* {@code false} otherwise.
@@ -1467,6 +1509,25 @@ public final class MediaProjectionManagerService extends SystemService
}
}
+ private void dispatchEvent(
+ @NonNull MediaProjectionEvent event,
+ @Nullable MediaProjectionInfo info,
+ @Nullable ContentRecordingSession session) {
+ if (!Flags.showStopDialogPostCallEnd()) {
+ Slog.d(
+ TAG,
+ "Event dispatch skipped. Reason: Flag showStopDialogPostCallEnd "
+ + "is disabled. Event details: "
+ + event);
+ return;
+ }
+ synchronized (mLock) {
+ for (IMediaProjectionWatcherCallback callback : mWatcherCallbacks.values()) {
+ mHandler.post(new WatcherEventCallback(callback, event, info, session));
+ }
+ }
+ }
+
public void dispatchSession(
@NonNull MediaProjectionInfo projectionInfo,
@Nullable ContentRecordingSession session) {
@@ -1593,6 +1654,41 @@ public final class MediaProjectionManagerService extends SystemService
}
}
+ private static final class WatcherEventCallback implements Runnable {
+ private final IMediaProjectionWatcherCallback mCallback;
+ private final MediaProjectionEvent mEvent;
+ private final MediaProjectionInfo mProjectionInfo;
+ private final ContentRecordingSession mSession;
+
+ WatcherEventCallback(
+ @NonNull IMediaProjectionWatcherCallback callback,
+ @NonNull MediaProjectionEvent event,
+ @Nullable MediaProjectionInfo projectionInfo,
+ @Nullable ContentRecordingSession session) {
+ mCallback = callback;
+ mEvent = event;
+ mProjectionInfo = projectionInfo;
+ mSession = session;
+ }
+
+ @Override
+ public void run() {
+ if (!Flags.showStopDialogPostCallEnd()) {
+ Slog.d(
+ TAG,
+ "Not running WatcherEventCallback. Reason: Flag "
+ + "showStopDialogPostCallEnd is disabled. "
+ );
+ return;
+ }
+ try {
+ mCallback.onMediaProjectionEvent(mEvent, mProjectionInfo, mSession);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify MediaProjectionEvent change", e);
+ }
+ }
+ }
+
private static final class WatcherSessionCallback implements Runnable {
private final IMediaProjectionWatcherCallback mCallback;
private final MediaProjectionInfo mProjectionInfo;
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java b/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
index c018e6bc1dc7..2e0bb4f88485 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
@@ -95,6 +95,11 @@ public class MediaProjectionStopController {
}
}
+ /** Checks if the given stop reason corresponds to a call ending. */
+ public boolean isStopReasonCallEnd(int stopReason) {
+ return stopReason == STOP_REASON_CALL_END;
+ }
+
/**
* Checks whether the given projection grant is exempt from stopping restrictions.
*/
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 7de2815eba6b..1d376b4bdfd5 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3612,13 +3612,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final long token = Binder.clearCallingIdentity();
try {
config = mCarrierConfigManager.getConfigForSubId(subId);
- tm = mContext.getSystemService(TelephonyManager.class);
+ tm = mContext.getSystemService(TelephonyManager.class).createForSubscriptionId(subId);
} finally {
Binder.restoreCallingIdentity(token);
}
- // First check: does caller have carrier privilege?
- if (tm != null && tm.hasCarrierPrivileges(subId)) {
+ // First check: does callingPackage have carrier privilege?
+ // Note that we can't call TelephonyManager.hasCarrierPrivileges() which will check if
+ // ourself has carrier privileges
+ if (tm != null && (tm.checkCarrierPrivilegesForPackage(callingPackage)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS)) {
return;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 588e87924b7d..1b1c99cf1410 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -258,7 +258,6 @@ import android.metrics.LogMaker;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -632,6 +631,8 @@ public class NotificationManagerService extends SystemService {
// Minium number of sparse groups for a package before autogrouping them
private static final int AUTOGROUP_SPARSE_GROUPS_AT_COUNT = 3;
+ private static final Duration ZEN_BROADCAST_DELAY = Duration.ofMillis(250);
+
private IActivityManager mAm;
private ActivityTaskManagerInternal mAtm;
private ActivityManager mActivityManager;
@@ -2834,33 +2835,25 @@ public class NotificationManagerService extends SystemService {
}
private void registerNetworkCallback() {
- NetworkRequest request = new NetworkRequest.Builder().addTransportType(
- NetworkCapabilities.TRANSPORT_WIFI).build();
- mConnectivityManager.registerNetworkCallback(request,
+ mConnectivityManager.registerDefaultNetworkCallback(
new ConnectivityManager.NetworkCallback() {
- // Need to post to another thread, as we can't call synchronous ConnectivityManager
- // methods from the callback itself, due to potential race conditions.
- @Override
- public void onAvailable(@NonNull Network network) {
- mHandler.post(() -> updateWifiConnectionState());
- }
- @Override
- public void onLost(@NonNull Network network) {
- mHandler.post(() -> updateWifiConnectionState());
- }
- });
- updateWifiConnectionState();
+ @Override
+ public void onCapabilitiesChanged(@NonNull Network network,
+ @NonNull NetworkCapabilities capabilities) {
+ updateWifiConnectionState(capabilities);
+ }
+ @Override
+ public void onLost(@NonNull Network network) {
+ mConnectedToWifi = false;
+ }
+ }, mHandler);
}
@VisibleForTesting()
- void updateWifiConnectionState() {
- Network current = mConnectivityManager.getActiveNetwork();
- NetworkCapabilities capabilities = mConnectivityManager.getNetworkCapabilities(current);
- if (current == null || capabilities == null) {
- mConnectedToWifi = false;
- return;
- }
- mConnectedToWifi = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
+ void updateWifiConnectionState(NetworkCapabilities capabilities) {
+ mConnectedToWifi = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
+ && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
+ && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED);
}
/**
@@ -3178,6 +3171,24 @@ public class NotificationManagerService extends SystemService {
sendRegisteredOnlyBroadcast(new Intent(action));
}
+ /**
+ * Schedules a broadcast to be sent to runtime receivers and DND-policy-access packages. The
+ * broadcast will be sent after {@link #ZEN_BROADCAST_DELAY}, unless a new broadcast is
+ * scheduled in the interim, in which case the previous one is dropped and the waiting period
+ * is <em>restarted</em>.
+ *
+ * <p>Note that this uses <em>equality of the {@link Intent#getAction}</em> as the criteria for
+ * deduplicating pending broadcasts, ignoring the extras and anything else. This is intentional
+ * so that e.g. rapidly changing some value A -> B -> C will only produce a broadcast for C
+ * (instead of every time because the extras are different).
+ */
+ private void sendZenBroadcastWithDelay(Intent intent) {
+ String token = "zen_broadcast:" + intent.getAction();
+ mHandler.removeCallbacksAndEqualMessages(token);
+ mHandler.postDelayed(() -> sendRegisteredOnlyBroadcast(intent), token,
+ ZEN_BROADCAST_DELAY.toMillis());
+ }
+
private void sendRegisteredOnlyBroadcast(Intent baseIntent) {
int[] userIds = mUmInternal.getProfileIds(mAmi.getCurrentUserId(), true);
if (Flags.nmBinderPerfReduceZenBroadcasts()) {
@@ -3371,14 +3382,25 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mNotificationLock")
private void updateEffectsSuppressorLocked() {
+ final long oldSuppressedEffects = mZenModeHelper.getSuppressedEffects();
final long updatedSuppressedEffects = calculateSuppressedEffects();
- if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
+ if (updatedSuppressedEffects == oldSuppressedEffects) return;
+
final List<ComponentName> suppressors = getSuppressors();
ZenLog.traceEffectsSuppressorChanged(
- mEffectsSuppressors, suppressors, updatedSuppressedEffects);
- mEffectsSuppressors = suppressors;
+ mEffectsSuppressors, suppressors, oldSuppressedEffects, updatedSuppressedEffects);
mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
- sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
+
+ if (Flags.nmBinderPerfThrottleEffectsSuppressorBroadcast()) {
+ if (!suppressors.equals(mEffectsSuppressors)) {
+ mEffectsSuppressors = suppressors;
+ sendZenBroadcastWithDelay(
+ new Intent(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED));
+ }
+ } else {
+ mEffectsSuppressors = suppressors;
+ sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
+ }
}
private void exitIdle() {
@@ -3500,12 +3522,18 @@ public class NotificationManagerService extends SystemService {
}
private ArrayList<ComponentName> getSuppressors() {
- ArrayList<ComponentName> names = new ArrayList<ComponentName>();
+ ArrayList<ComponentName> names = new ArrayList<>();
for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
ArraySet<ComponentName> serviceInfoList = mListenersDisablingEffects.valueAt(i);
for (ComponentName info : serviceInfoList) {
- names.add(info);
+ if (Flags.nmBinderPerfThrottleEffectsSuppressorBroadcast()) {
+ if (!names.contains(info)) {
+ names.add(info);
+ }
+ } else {
+ names.add(info);
+ }
}
}
@@ -7221,7 +7249,7 @@ public class NotificationManagerService extends SystemService {
if (!mAssistants.isAdjustmentAllowed(potentialKey)) {
toRemove.add(potentialKey);
}
- if (notificationClassification() && adjustments.containsKey(KEY_TYPE)) {
+ if (notificationClassification() && potentialKey.equals(KEY_TYPE)) {
mAssistants.setNasUnsupportedDefaults(r.getSbn().getNormalizedUserId());
if (!mAssistants.isAdjustmentKeyTypeAllowed(adjustments.getInt(KEY_TYPE))) {
toRemove.add(potentialKey);
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index 7e853d9d2d0b..49f93b8b7c16 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -140,8 +140,9 @@ public class ZenLog {
}
public static void traceEffectsSuppressorChanged(List<ComponentName> oldSuppressors,
- List<ComponentName> newSuppressors, long suppressedEffects) {
- append(TYPE_SUPPRESSOR_CHANGED, "suppressed effects:" + suppressedEffects + ","
+ List<ComponentName> newSuppressors, long oldSuppressedEffects, long suppressedEffects) {
+ append(TYPE_SUPPRESSOR_CHANGED, "suppressed effects:"
+ + oldSuppressedEffects + "->" + suppressedEffects + ","
+ componentListToString(oldSuppressors) + "->"
+ componentListToString(newSuppressors));
}
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 822ff48c831c..048f2b6b0cbc 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -182,6 +182,16 @@ flag {
}
flag {
+ name: "nm_binder_perf_throttle_effects_suppressor_broadcast"
+ namespace: "systemui"
+ description: "Delay sending the ACTION_EFFECTS_SUPPRESSOR_CHANGED broadcast if it changes too often"
+ bug: "371776935"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "fix_calling_uid_from_cps"
namespace: "systemui"
description: "Correctly checks zen rule ownership when a CPS notifies with a Condition"
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 6303ecd53dbb..8710438d76b3 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -1064,10 +1064,12 @@ public final class OverlayManagerService extends SystemService {
return;
}
try {
- dumpState.setUserId(Integer.parseInt(args[opti]));
+ final int userId = UserHandle.parseUserArg(args[opti]);
+ final int realUserId = handleIncomingUser(userId, "dump");
+ dumpState.setUserId(realUserId);
opti++;
- } catch (NumberFormatException e) {
- pw.println("Error: user argument is not a number: " + args[opti]);
+ } catch (Exception e) {
+ pw.println("Error: " + e.getMessage());
return;
}
} else if ("--verbose".equals(opt)) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b85e6894b910..a2c53e56b9c9 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -57,6 +57,10 @@ import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SpecialUsers.CanBeALL;
+import android.annotation.SpecialUsers.CanBeCURRENT;
+import android.annotation.SpecialUsers.CanBeNULL;
+import android.annotation.SpecialUsers.CannotBeSpecialUser;
import android.annotation.StringRes;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -952,7 +956,7 @@ public class UserManagerService extends IUserManager.Stub {
private final UserVisibilityMediator mUserVisibilityMediator;
@GuardedBy("mUsersLock")
- private @UserIdInt int mBootUser = UserHandle.USER_NULL;
+ private @CanBeNULL @UserIdInt int mBootUser = UserHandle.USER_NULL;
private static UserManagerService sInstance;
@@ -1333,12 +1337,12 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
- public @UserIdInt int getMainUserId() {
+ public @CanBeNULL @UserIdInt int getMainUserId() {
checkQueryOrCreateUsersPermission("get main user id");
return getMainUserIdUnchecked();
}
- private @UserIdInt int getMainUserIdUnchecked() {
+ private @CanBeNULL @UserIdInt int getMainUserIdUnchecked() {
synchronized (mUsersLock) {
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
@@ -1351,7 +1355,7 @@ public class UserManagerService extends IUserManager.Stub {
return UserHandle.USER_NULL;
}
- private @UserIdInt int getPrivateProfileUserId() {
+ private @CanBeNULL @UserIdInt int getPrivateProfileUserId() {
synchronized (mUsersLock) {
for (int userId : getUserIds()) {
UserInfo userInfo = getUserInfoLU(userId);
@@ -1455,7 +1459,7 @@ public class UserManagerService extends IUserManager.Stub {
"No switchable users found", USER_OPERATION_ERROR_UNKNOWN);
}
- private @UserIdInt int getFirstSwitchableUser(boolean fullUserOnly) {
+ private @CanBeNULL @UserIdInt int getFirstSwitchableUser(boolean fullUserOnly) {
synchronized (mUsersLock) {
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
@@ -1472,7 +1476,7 @@ public class UserManagerService extends IUserManager.Stub {
@Override
- public int getPreviousFullUserToEnterForeground() {
+ public @CanBeNULL @UserIdInt int getPreviousFullUserToEnterForeground() {
checkQueryOrCreateUsersPermission("get previous user");
int previousUser = UserHandle.USER_NULL;
long latestEnteredTime = 0;
@@ -1496,13 +1500,13 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
- public @UserIdInt int getCommunalProfileId() {
+ public @CanBeNULL @UserIdInt int getCommunalProfileId() {
checkQueryOrCreateUsersPermission("get communal profile user id");
return getCommunalProfileIdUnchecked();
}
/** Returns the currently-designated communal profile, or USER_NULL if not present. */
- private @UserIdInt int getCommunalProfileIdUnchecked() {
+ private @CanBeNULL @UserIdInt int getCommunalProfileIdUnchecked() {
synchronized (mUsersLock) {
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
@@ -2675,7 +2679,7 @@ public class UserManagerService extends IUserManager.Stub {
* {@link ActivityManagerInternal} is not available yet.
*/
@VisibleForTesting
- int getCurrentUserId() {
+ @CanBeNULL @UserIdInt int getCurrentUserId() {
ActivityManagerInternal activityManagerInternal = getActivityManagerInternal();
if (activityManagerInternal == null) {
Slog.w(LOG_TAG, "getCurrentUserId() called too early, ActivityManagerInternal"
@@ -2918,7 +2922,16 @@ public class UserManagerService extends IUserManager.Stub {
* switchable.
*/
public @UserManager.UserSwitchabilityResult int getUserSwitchability(int userId) {
- checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserSwitchability");
+ if (Flags.getUserSwitchabilityPermission()) {
+ if (!hasManageUsersOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)) {
+ throw new SecurityException(
+ "You need MANAGE_USERS or INTERACT_ACROSS_USERS permission to "
+ + "getUserSwitchability");
+ }
+ } else {
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
+ "getUserSwitchability");
+ }
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("getUserSwitchability-" + userId);
@@ -2974,27 +2987,27 @@ public class UserManagerService extends IUserManager.Stub {
}
@VisibleForTesting
- boolean isUserSwitcherEnabled(@UserIdInt int mUserId) {
+ boolean isUserSwitcherEnabled(@UserIdInt int userId) {
boolean multiUserSettingOn = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.USER_SWITCHER_ENABLED,
Resources.getSystem().getBoolean(com.android.internal
.R.bool.config_showUserSwitcherByDefault) ? 1 : 0) != 0;
return UserManager.supportsMultipleUsers()
- && !hasUserRestriction(DISALLOW_USER_SWITCH, mUserId)
+ && !hasUserRestriction(DISALLOW_USER_SWITCH, userId)
&& !UserManager.isDeviceInDemoMode(mContext)
&& multiUserSettingOn;
}
@Override
public boolean isUserSwitcherEnabled(boolean showEvenIfNotActionable,
- @UserIdInt int mUserId) {
- if (!isUserSwitcherEnabled(mUserId)) {
+ @UserIdInt int userId) {
+ if (!isUserSwitcherEnabled(userId)) {
return false;
}
// The feature is enabled. But is it worth showing?
return showEvenIfNotActionable
- || !hasUserRestriction(UserManager.DISALLOW_ADD_USER, mUserId) // Can add new user
+ || !hasUserRestriction(UserManager.DISALLOW_ADD_USER, userId) // Can add new user
|| areThereMultipleSwitchableUsers(); // There are switchable users
}
@@ -3427,7 +3440,8 @@ public class UserManagerService extends IUserManager.Stub {
}
@VisibleForTesting
- void setUserRestrictionInner(int userId, @NonNull String key, boolean value) {
+ void setUserRestrictionInner(
+ @CanBeALL @UserIdInt int userId, @NonNull String key, boolean value) {
if (!UserRestrictionsUtils.isValidRestriction(key)) {
Slog.e(LOG_TAG, "Setting invalid restriction " + key);
return;
@@ -3520,10 +3534,11 @@ public class UserManagerService extends IUserManager.Stub {
/** @return a specific user restriction that's in effect currently. */
@Override
public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) {
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "hasUserRestriction");
+ // TODO(b/390455855): Should this be (!userExists(userId) && userId != UserHandle.USER_ALL)?
if (!userExists(userId)) {
return false;
}
- checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "hasUserRestriction");
return mLocalService.hasUserRestriction(restrictionKey, userId);
}
@@ -5610,7 +5625,7 @@ public class UserManagerService extends IUserManager.Stub {
private @NonNull UserInfo createUserInternal(
@Nullable String name, @NonNull String userType,
- @UserInfoFlag int flags, @UserIdInt int parentId,
+ @UserInfoFlag int flags, @CanBeNULL @UserIdInt int parentId,
@Nullable String[] disallowedPackages)
throws UserManager.CheckedUserOperationException {
@@ -5643,8 +5658,8 @@ public class UserManagerService extends IUserManager.Stub {
private @NonNull UserInfo createUserInternalUnchecked(
@Nullable String name, @NonNull String userType, @UserInfoFlag int flags,
- @UserIdInt int parentId, boolean preCreate, @Nullable String[] disallowedPackages,
- @Nullable Object token)
+ @CanBeNULL @UserIdInt int parentId, boolean preCreate,
+ @Nullable String[] disallowedPackages, @Nullable Object token)
throws UserManager.CheckedUserOperationException {
final int noneUserId = -1;
@@ -6227,9 +6242,11 @@ public class UserManagerService extends IUserManager.Stub {
if (UserManager.getMaxSupportedUsers() > 1) {
data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.MULTI_USER_INFO,
UserManager.getMaxSupportedUsers(),
+ // TODO(b/390455855): is USER_ALL really allowed here?
isUserSwitcherEnabled(UserHandle.USER_ALL),
UserManager.supportsMultipleUsers()
&& !hasUserRestriction(UserManager.DISALLOW_ADD_USER,
+ // TODO(b/390455855): is USER_ALL really allowed here?
UserHandle.USER_ALL)));
}
} else {
@@ -7580,8 +7597,8 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- private void dumpUser(PrintWriter pw, @UserIdInt int userId, StringBuilder sb, long now,
- long nowRealtime) {
+ private void dumpUser(PrintWriter pw, @CanBeCURRENT @UserIdInt int userId, StringBuilder sb,
+ long now, long nowRealtime) {
if (userId == UserHandle.USER_CURRENT) {
final int currentUserId = getCurrentUserId();
pw.print("Current user: ");
@@ -7755,7 +7772,7 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
- public void setUserRestriction(int userId, @NonNull String key, boolean value) {
+ public void setUserRestriction(@UserIdInt int userId, @NonNull String key, boolean value) {
UserManagerService.this.setUserRestrictionInner(userId, key, value);
}
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index 441d3eaf2348..142d919da455 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -24,6 +24,8 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.ViewConfiguration;
+import com.android.hardware.input.Flags;
+
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -355,6 +357,19 @@ public final class SingleKeyGestureDetector {
}
if (event.getKeyCode() == mActiveRule.mKeyCode) {
+ if (Flags.abortSlowMultiPress()
+ && (event.getEventTime() - mLastDownTime
+ >= mActiveRule.getLongPressTimeoutMs())) {
+ // In this case, we are either on a first long press (but long press behavior is not
+ // supported for this rule), or, on a non-first press that is at least as long as
+ // the long-press duration. Thus, we will cancel the multipress gesture.
+ if (DEBUG) {
+ Log.d(TAG, "The duration of the press is too slow. Resetting.");
+ }
+ reset();
+ return false;
+ }
+
// key-up action should always be triggered if not processed by long press.
MessageObject object = new MessageObject(mActiveRule, mActiveRule.mKeyCode,
mKeyPressCounter, event);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index a8deeeac311d..8a3ee61d5d80 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -772,6 +772,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private final ComponentName mImageWallpaper;
/**
+ * Name of the component that is used when the user-selected wallpaper is incompatible with the
+ * display's resolution or aspect ratio.
+ */
+ @Nullable private final ComponentName mFallbackWallpaperComponent;
+
+ /**
* Default image wallpaper shall never changed after system service started, caching it when we
* first read the image file.
*/
@@ -1601,6 +1607,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mShuttingDown = false;
mImageWallpaper = ComponentName.unflattenFromString(
context.getResources().getString(R.string.image_wallpaper_component));
+ if (enableConnectedDisplaysWallpaper()) {
+ mFallbackWallpaperComponent = ComponentName.unflattenFromString(
+ context.getResources().getString(R.string.fallback_wallpaper_component));
+ } else {
+ mFallbackWallpaperComponent = null;
+ }
ComponentName defaultComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context);
mDefaultWallpaperComponent = defaultComponent == null ? mImageWallpaper : defaultComponent;
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
@@ -3613,6 +3625,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (componentName != null && !componentName.equals(mImageWallpaper)) {
// The requested component is not the static wallpaper service, so make sure it's
// actually a wallpaper service.
+ if (mFallbackWallpaperComponent != null
+ && componentName.equals(mFallbackWallpaperComponent)) {
+ // The fallback wallpaper does not declare WallpaperService.SERVICE_INTERFACE
+ // action in its intent filter to prevent it from being listed in the wallpaper
+ // picker. And thus, use explicit intent to query the metadata.
+ intent = new Intent().setComponent(mFallbackWallpaperComponent);
+ }
List<ResolveInfo> ris =
mIPackageManager.queryIntentServices(intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
@@ -4100,8 +4119,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mFallbackWallpaper.allowBackup = false;
mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
mFallbackWallpaper.mBindSource = BindSource.INITIALIZE_FALLBACK;
- bindWallpaperComponentLocked(mDefaultWallpaperComponent, true, false,
- mFallbackWallpaper, null);
+ if (mFallbackWallpaperComponent == null) {
+ bindWallpaperComponentLocked(mDefaultWallpaperComponent, true, false,
+ mFallbackWallpaper, null);
+ } else {
+ mFallbackWallpaper.mWhich = FLAG_SYSTEM | FLAG_LOCK;
+ bindWallpaperComponentLocked(mFallbackWallpaperComponent, true, false,
+ mFallbackWallpaper, null);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index dd769173fb34..12f553426c80 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -58,7 +58,6 @@ import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.graphics.Region;
import android.os.Binder;
import android.os.Build;
@@ -69,7 +68,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.SystemClock;
-import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -79,7 +77,6 @@ import android.view.Display;
import android.view.MagnificationSpec;
import android.view.Surface;
import android.view.ViewConfiguration;
-import android.view.WindowInfo;
import android.view.WindowManager;
import android.view.WindowManager.TransitionFlags;
import android.view.WindowManager.TransitionType;
@@ -557,9 +554,6 @@ final class AccessibilityController {
private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
private static final boolean DEBUG_DISPLAY_SIZE = false;
- private static final boolean DEBUG_LAYERS = false;
- private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
- private static final boolean DEBUG_VIEWPORT_WINDOW = false;
private final Rect mTempRect1 = new Rect();
private final Rect mTempRect2 = new Rect();
@@ -579,8 +573,6 @@ final class AccessibilityController {
private final MagnificationCallbacks mCallbacks;
private final UserContextChangedNotifier mUserContextChangedNotifier;
- private final long mLongAnimationDuration;
-
private boolean mIsFullscreenMagnificationActivated = false;
private final Region mMagnificationRegion = new Region();
private final Region mOldMagnificationRegion = new Region();
@@ -593,7 +585,6 @@ final class AccessibilityController {
private final Point mScreenSize = new Point();
private final SparseArray<WindowState> mTempWindowStates =
new SparseArray<WindowState>();
- private final RectF mTempRectF = new RectF();
private final Matrix mTempMatrix = new Matrix();
DisplayMagnifier(WindowManagerService windowManagerService,
@@ -609,8 +600,6 @@ final class AccessibilityController {
mUserContextChangedNotifier = new UserContextChangedNotifier(mHandler);
mAccessibilityTracing =
AccessibilityController.getAccessibilityControllerInternal(mService);
- mLongAnimationDuration = mDisplayContext.getResources().getInteger(
- com.android.internal.R.integer.config_longAnimTime);
if (mDisplayContext.getResources().getConfiguration().isScreenRound()) {
mCircularPath = new Path();
@@ -840,10 +829,6 @@ final class AccessibilityController {
outMagnificationRegion.set(mMagnificationRegion);
}
- boolean isMagnifying() {
- return mMagnificationSpec.scale > 1.0f;
- }
-
void destroy() {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(LOG_TAG + ".destroy", FLAGS_MAGNIFICATION_CALLBACK);
@@ -1172,12 +1157,6 @@ final class AccessibilityController {
private static final boolean DEBUG = false;
- private final Set<IBinder> mTempBinderSet = new ArraySet<>();
-
- private final Region mTempRegion = new Region();
-
- private final Region mTempRegion2 = new Region();
-
private final WindowManagerService mService;
private final Handler mHandler;
@@ -1243,11 +1222,10 @@ final class AccessibilityController {
Slog.i(LOG_TAG, "computeChangedWindows()");
}
- List<WindowInfo> windows = null;
final List<AccessibilityWindow> visibleWindows = new ArrayList<>();
final Point screenSize = new Point();
final int topFocusedDisplayId;
- IBinder topFocusedWindowToken = null;
+ final IBinder topFocusedWindowToken;
synchronized (mService.mGlobalLock) {
final WindowState topFocusedWindowState = getTopFocusWindow();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 8e4d4be693f8..c7d4467a6e98 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -103,6 +103,7 @@ import android.app.PendingIntent;
import android.app.ProfilerInfo;
import android.app.WaitResult;
import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
@@ -233,6 +234,7 @@ class ActivityStarter {
// The task display area to launch the activity onto, barring any strong reason to do otherwise.
private TaskDisplayArea mPreferredTaskDisplayArea;
+ @WindowingMode
private int mPreferredWindowingMode;
private Task mInTask;
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 4bcba13448e9..f465c95addb7 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -899,7 +899,9 @@ class InsetsPolicy {
}
@Override
- public void notifyAnimationRunningStateChanged(boolean running) {
+ public void notifyAnimationRunningStateChanged(boolean running,
+ @InsetsController.AnimationType int animationType,
+ @InsetsType int insetsTypes) {
mInsetsAnimationRunning = running;
}
}
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index ba1401ab3978..4f5c0c8ecf6e 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -28,6 +28,7 @@ import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityOptions;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.pm.ActivityInfo.WindowLayout;
import android.graphics.Rect;
@@ -186,6 +187,7 @@ class LaunchParamsController {
TaskDisplayArea mPreferredTaskDisplayArea;
/** The windowing mode to be in. */
+ @WindowingMode
int mWindowingMode;
/** Sets values back to default. {@link #isEmpty} will return {@code true} once called. */
diff --git a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java
index efc68aac0323..00e1c01bbadb 100644
--- a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java
+++ b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java
@@ -22,6 +22,7 @@ import static com.android.internal.protolog.WmProtoLogGroups.WM_ERROR;
import android.media.projection.IMediaProjectionManager;
import android.media.projection.IMediaProjectionWatcherCallback;
+import android.media.projection.MediaProjectionEvent;
import android.media.projection.MediaProjectionInfo;
import android.os.Binder;
import android.os.IBinder;
@@ -84,6 +85,12 @@ public class ScreenRecordingCallbackController {
public void onRecordingSessionSet(MediaProjectionInfo mediaProjectionInfo,
ContentRecordingSession contentRecordingSession) {
}
+
+ @Override
+ public void onMediaProjectionEvent(
+ MediaProjectionEvent event,
+ MediaProjectionInfo mediaProjectionInfo,
+ ContentRecordingSession session) {}
}
ScreenRecordingCallbackController(WindowManagerService wms) {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 0ecd0251ca94..3b79c54f1c73 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -89,8 +89,9 @@ class WallpaperWindowToken extends WindowToken {
// Similar to Task.prepareSurfaces, outside of transitions we need to apply visibility
// changes directly. In transitions the transition player will take care of applying the
// visibility change.
- if (!mTransitionController.inTransition(this)) {
- getSyncTransaction().setVisibility(mSurfaceControl, isVisible());
+ if (!mTransitionController.isCollecting(this)
+ && !mTransitionController.isPlayingTarget(this)) {
+ getPendingTransaction().setVisibility(mSurfaceControl, isVisible());
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index c77b1d9a7bcf..7a8230f1f963 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -41,6 +41,7 @@ import android.view.Display;
import android.view.IInputFilter;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IWindow;
+import android.view.InsetsController;
import android.view.MagnificationSpec;
import android.view.RemoteAnimationTarget;
import android.view.Surface;
@@ -469,6 +470,24 @@ public abstract class WindowManagerInternal {
public abstract void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion);
/**
+ * Set by the autofill service to observe changes in the ime animations.
+ *
+ * @param listener The callbacks to invoke.
+ */
+ public abstract void setImeInsetsAnimationChangeListener(
+ @Nullable ImeInsetsAnimationChangeListener listener);
+
+ /** Listener for changes in ime insets animation */
+ public interface ImeInsetsAnimationChangeListener {
+
+ /** Notify on start of animation */
+ void onAnimationStart(@InsetsController.AnimationType int animationType, int userId);
+
+ /** Notify on end of animation */
+ void onAnimationEnd(@InsetsController.AnimationType int animationType, int userId);
+ }
+
+ /**
* Sets a callback for observing which windows are touchable for the purposes
* of accessibility on specified display.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5de0e9b6ed93..28ea3b0bf6ba 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.service.autofill.Flags.improveFillDialogAconfig;
import static android.Manifest.permission.ACCESS_SURFACE_FLINGER;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.INPUT_CONSUMER;
@@ -277,6 +278,7 @@ import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputWindowHandle;
+import android.view.InsetsController;
import android.view.InsetsFrameProvider;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -309,6 +311,7 @@ import android.window.ActivityWindowInfo;
import android.window.AddToSurfaceSyncGroupResult;
import android.window.ClientWindowFrames;
import android.window.ConfigurationChangeSetting;
+import android.window.DesktopModeFlags;
import android.window.IGlobalDragListener;
import android.window.IScreenRecordingCallback;
import android.window.ISurfaceSyncGroupCompletedListener;
@@ -792,6 +795,9 @@ public class WindowManagerService extends IWindowManager.Stub
final TrustedPresentationListenerController mTrustedPresentationListenerController =
new TrustedPresentationListenerController();
+ private WindowManagerInternal.ImeInsetsAnimationChangeListener
+ mImeInsetsAnimationChangeListener;
+
@VisibleForTesting
final class SettingsObserver extends ContentObserver {
private final Uri mDisplayInversionEnabledUri =
@@ -820,6 +826,8 @@ public class WindowManagerService extends IWindowManager.Stub
DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH);
private final Uri mMaximumObscuringOpacityForTouchUri = Settings.Global.getUriFor(
Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
+ private final Uri mDevelopmentOverrideDesktopExperienceUri = Settings.Global.getUriFor(
+ Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_EXPERIENCE_FEATURES);
public SettingsObserver() {
super(new Handler());
@@ -847,6 +855,8 @@ public class WindowManagerService extends IWindowManager.Stub
UserHandle.USER_ALL);
resolver.registerContentObserver(mMaximumObscuringOpacityForTouchUri, false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(mDevelopmentOverrideDesktopExperienceUri, false, this,
+ UserHandle.USER_ALL);
}
@Override
@@ -890,6 +900,11 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
+ if (mDevelopmentOverrideDesktopExperienceUri.equals(uri)) {
+ updateDevelopmentOverrideDesktopExperience();
+ return;
+ }
+
@UpdateAnimationScaleMode
final int mode;
if (mWindowAnimationScaleUri.equals(uri)) {
@@ -956,6 +971,16 @@ public class WindowManagerService extends IWindowManager.Stub
mAtmService.mForceResizableActivities = forceResizable;
}
+ void updateDevelopmentOverrideDesktopExperience() {
+ ContentResolver resolver = mContext.getContentResolver();
+ final int overrideDesktopMode = Settings.Global.getInt(resolver,
+ Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_EXPERIENCE_FEATURES,
+ DesktopModeFlags.ToggleOverride.OVERRIDE_UNSET.getSetting());
+
+ SystemProperties.set(DesktopModeFlags.SYSTEM_PROPERTY_NAME,
+ Integer.toString(overrideDesktopMode));
+ }
+
void updateDevEnableNonResizableMultiWindow() {
ContentResolver resolver = mContext.getContentResolver();
final boolean devEnableNonResizableMultiWindow = Settings.Global.getInt(resolver,
@@ -8602,6 +8627,14 @@ public class WindowManagerService extends IWindowManager.Stub
// WMS.takeAssistScreenshot takes care of the locking.
return WindowManagerService.this.takeAssistScreenshot(windowTypesToExclude);
}
+
+ @Override
+ public void setImeInsetsAnimationChangeListener(
+ @Nullable WindowManagerInternal.ImeInsetsAnimationChangeListener listener) {
+ synchronized (mGlobalLock) {
+ mImeInsetsAnimationChangeListener = listener;
+ }
+ }
}
private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy {
@@ -10159,6 +10192,24 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
+ public void notifyImeInsetsAnimationStateChanged(
+ boolean running, @InsetsController.AnimationType int animationType) {
+ if (improveFillDialogAconfig()) {
+ synchronized (mGlobalLock) {
+ if (mImeInsetsAnimationChangeListener == null) {
+ return;
+ }
+ if (running) {
+ mImeInsetsAnimationChangeListener.onAnimationStart(
+ animationType, mCurrentUserId);
+ } else {
+ mImeInsetsAnimationChangeListener.onAnimationEnd(animationType, mCurrentUserId);
+ }
+ }
+ }
+ }
+
boolean getDisableSecureWindows() {
return mDisableSecureWindows;
}
diff --git a/services/core/jni/com_android_server_utils_AnrTimer.cpp b/services/core/jni/com_android_server_utils_AnrTimer.cpp
index 2add5b09f15b..24909ac6150b 100644
--- a/services/core/jni/com_android_server_utils_AnrTimer.cpp
+++ b/services/core/jni/com_android_server_utils_AnrTimer.cpp
@@ -101,6 +101,9 @@ nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
+// The current process. This is cached here on startup.
+const pid_t sThisProcess = getpid();
+
// Return true if the process exists and false if we cannot know.
bool processExists(pid_t pid) {
char path[PATH_MAX];
@@ -726,7 +729,7 @@ class AnrTimerService::Timer {
uid(uid),
timeout(timeout),
extend(extend),
- freeze(pid != 0 && freeze),
+ freeze(freeze),
split(trace.earlyTimeout),
action(trace.action),
status(Running),
@@ -1188,8 +1191,11 @@ const char* AnrTimerService::statusString(Status s) {
}
AnrTimerService::timer_id_t AnrTimerService::start(int pid, int uid, nsecs_t timeout) {
+ // Use the freezer only if the pid is not 0 (a nonsense value) and the pid is not self.
+ // Freezing the current process is a fatal error.
+ bool useFreezer = freeze_ && (pid != 0) && (pid != sThisProcess);
AutoMutex _l(lock_);
- Timer t(pid, uid, timeout, extend_, freeze_, tracer_.getConfig(pid));
+ Timer t(pid, uid, timeout, extend_, useFreezer, tracer_.getConfig(pid));
insertLocked(t);
t.start();
counters_.started++;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 25e9f8a38f89..c974d9e1dc87 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1799,7 +1799,7 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(LogcatManagerService.class);
t.traceEnd();
- if (!isWatch && !isTv && !isAutomotive
+ if (!isWatch && !isTv && !isAutomotive && !isDesktop
&& android.security.Flags.aflApi()) {
t.traceBegin("StartIntrusionDetectionService");
mSystemServiceManager.startService(IntrusionDetectionService.class);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayBackupHelperTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayBackupHelperTest.kt
new file mode 100644
index 000000000000..26060a406aa0
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayBackupHelperTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 androidx.test.filters.SmallTest
+import android.hardware.display.DisplayManagerInternal
+import android.util.AtomicFileOutputStream
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.never
+
+@SmallTest
+class DisplayBackupHelperTest {
+ private val mockInjector = mock<DisplayBackupHelper.Injector>()
+ private val mockDmsInternal = mock<DisplayManagerInternal>()
+ private val mockWriteTopologyFile = mock<AtomicFileOutputStream>()
+ private val byteArray = byteArrayOf(0b00000001, 0b00000010, 0b00000011)
+ private val helper = createBackupHelper(0, byteArray)
+
+ @Test
+ fun testBackupDisplayReturnsBytes() {
+ assertThat(helper.getBackupPayload("display")).isEqualTo(byteArray)
+ }
+
+ @Test
+ fun testBackupSomethingReturnsNull() {
+ assertThat(helper.getBackupPayload("something")).isNull()
+ }
+
+ @Test
+ fun testBackupDisplayReturnsNullWhenFlagDisabled() {
+ whenever(mockInjector.isDisplayTopologyFlagEnabled()).thenReturn(false)
+ assertThat(helper.getBackupPayload("display")).isNull()
+ }
+
+ @Test
+ fun testRestoreDisplay() {
+ helper.applyRestoredPayload("display", byteArray)
+ verify(mockWriteTopologyFile).write(byteArray)
+ verify(mockWriteTopologyFile).markSuccess()
+ verify(mockDmsInternal).reloadTopologies(0)
+ }
+
+ @Test
+ fun testRestoreSomethingDoesNothing() {
+ helper.applyRestoredPayload("something", byteArray)
+ verify(mockWriteTopologyFile, never()).write(byteArray)
+ verify(mockWriteTopologyFile, never()).markSuccess()
+ verify(mockDmsInternal, never()).reloadTopologies(0)
+ }
+
+ @Test
+ fun testRestoreDisplayDoesNothingWhenFlagDisabled() {
+ whenever(mockInjector.isDisplayTopologyFlagEnabled()).thenReturn(false)
+ helper.applyRestoredPayload("display", byteArray)
+ verify(mockWriteTopologyFile, never()).write(byteArray)
+ verify(mockWriteTopologyFile, never()).markSuccess()
+ verify(mockDmsInternal, never()).reloadTopologies(0)
+ }
+
+ fun createBackupHelper(userId: Int, topologyToBackup: ByteArray): DisplayBackupHelper {
+ whenever(mockInjector.getDisplayManagerInternal()).thenReturn(mockDmsInternal)
+ whenever(mockInjector.readTopologyFile(userId)).thenReturn(topologyToBackup)
+ whenever(mockInjector.writeTopologyFile(userId)).thenReturn(mockWriteTopologyFile)
+ whenever(mockInjector.isDisplayTopologyFlagEnabled()).thenReturn(true)
+
+ return DisplayBackupHelper(userId, mockInjector)
+ }
+} \ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index e8b28ac19a85..a0e18ff15770 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -31,6 +31,7 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_C
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
+import static android.hardware.display.DisplayManagerGlobal.INTERNAL_EVENT_FLAG_TOPOLOGY_UPDATED;
import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_SYSTEM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.provider.Settings.Secure.MIRROR_BUILT_IN_DISPLAY;
@@ -413,6 +414,9 @@ public class DisplayManagerServiceTest {
.setStrictness(Strictness.LENIENT)
.spyStatic(SystemProperties.class)
.build();
+
+ private int mUniqueIdCount = 0;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -759,6 +763,83 @@ public class DisplayManagerServiceTest {
}
@Test
+ public void testCreateVirtualDisplayStealTopFocusDisabled() throws RemoteException {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+
+ // This is effectively the DisplayManager service published to ServiceManager.
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+
+ String uniqueId = "uniqueId --- Steal Top Focus Test";
+ int width = 600;
+ int height = 800;
+ int dpi = 320;
+ int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
+
+ when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY)).thenReturn(
+ PackageManager.PERMISSION_GRANTED);
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, width, height, dpi);
+ builder.setFlags(flags);
+ builder.setUniqueId(uniqueId);
+ int displayId = bs.createVirtualDisplay(builder.build(), /* callback= */ mMockAppToken,
+ /* projection= */ null, PACKAGE_NAME);
+ verify(mMockProjectionService, never()).setContentRecordingSession(any(),
+ nullable(IMediaProjection.class));
+
+ performTraversalInternal(displayManager);
+
+ // flush the handler
+ displayManager.getDisplayHandler().runWithScissors(() -> {}, /* now= */ 0);
+
+ DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
+ assertNotNull(ddi);
+ assertTrue((ddi.flags & DisplayDeviceInfo.FLAG_STEAL_TOP_FOCUS_DISABLED) != 0);
+ }
+
+ @Test
+ public void testCreateVirtualDisplayOwnFocus_nonOwnFocusDisplay() throws RemoteException {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+
+ // This is effectively the DisplayManager service published to ServiceManager.
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+
+ String uniqueId = "uniqueId --- Steal Top Focus Test -- nonOwnFocusDisplay";
+ int width = 600;
+ int height = 800;
+ int dpi = 320;
+ int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
+
+ when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY)).thenReturn(
+ PackageManager.PERMISSION_GRANTED);
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, width, height, dpi);
+ builder.setFlags(flags);
+ builder.setUniqueId(uniqueId);
+ int displayId = bs.createVirtualDisplay(builder.build(), /* callback= */ mMockAppToken,
+ /* projection= */ null, PACKAGE_NAME);
+ verify(mMockProjectionService, never()).setContentRecordingSession(any(),
+ nullable(IMediaProjection.class));
+
+ performTraversalInternal(displayManager);
+
+ // flush the handler
+ displayManager.getDisplayHandler().runWithScissors(() -> {}, /* now= */ 0);
+
+ DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
+ assertNotNull(ddi);
+ assertTrue((ddi.flags & DisplayDeviceInfo.FLAG_STEAL_TOP_FOCUS_DISABLED) == 0);
+ }
+
+ @Test
public void testCreateVirtualDisplayOwnFocus_checkDisplayDeviceInfo() throws RemoteException {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mBasicInjector);
@@ -2126,11 +2207,12 @@ public class DisplayManagerServiceTest {
@Test
public void test_displayChangedNotified_displayInfoFramerateOverridden() {
+ when(mMockFlags.isFramerateOverrideTriggersRrCallbacksEnabled()).thenReturn(false);
+
DisplayManagerService displayManager =
- new DisplayManagerService(mContext, mShortMockedInjector);
+ new DisplayManagerService(mContext, mBasicInjector);
DisplayManagerService.BinderService displayManagerBinderService =
displayManager.new BinderService();
- when(mMockFlags.isFramerateOverrideTriggersRrCallbacksEnabled()).thenReturn(false);
registerDefaultDisplays(displayManager);
displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
@@ -3786,6 +3868,7 @@ public class DisplayManagerServiceTest {
public void testSetDisplayTopology() {
manageDisplaysPermission(/* granted= */ true);
when(mMockFlags.isDisplayTopologyEnabled()).thenReturn(true);
+ when(mMockFlags.isDisplayContentModeManagementEnabled()).thenReturn(true);
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
DisplayManagerInternal localService = displayManager.new LocalService();
DisplayManagerService.BinderService displayManagerBinderService =
@@ -3814,6 +3897,7 @@ public class DisplayManagerServiceTest {
public void testShouldNotifyTopologyChanged() {
manageDisplaysPermission(/* granted= */ true);
when(mMockFlags.isDisplayTopologyEnabled()).thenReturn(true);
+ when(mMockFlags.isDisplayContentModeManagementEnabled()).thenReturn(true);
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
DisplayManagerService.BinderService displayManagerBinderService =
displayManager.new BinderService();
@@ -3822,19 +3906,22 @@ public class DisplayManagerServiceTest {
FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
displayManagerBinderService.registerCallbackWithEventMask(callback,
- DisplayManagerGlobal.INTERNAL_EVENT_FLAG_TOPOLOGY_UPDATED);
+ INTERNAL_EVENT_FLAG_TOPOLOGY_UPDATED);
waitForIdleHandler(handler);
- displayManagerBinderService.setDisplayTopology(new DisplayTopology());
- waitForIdleHandler(handler);
-
- assertThat(callback.receivedEvents()).containsExactly(TOPOLOGY_CHANGED_EVENT);
+ var topology = initDisplayTopology(displayManager, displayManagerBinderService, callback,
+ handler, /*shouldEmitTopologyChangeEvent=*/ true);
+ callback.clear();
+ callback.expectsEvent(TOPOLOGY_CHANGED_EVENT);
+ displayManagerBinderService.setDisplayTopology(topology);
+ callback.waitForExpectedEvent();
}
@Test
public void testShouldNotNotifyTopologyChanged_WhenClientIsNotSubscribed() {
manageDisplaysPermission(/* granted= */ true);
when(mMockFlags.isDisplayTopologyEnabled()).thenReturn(true);
+ when(mMockFlags.isDisplayContentModeManagementEnabled()).thenReturn(true);
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
DisplayManagerService.BinderService displayManagerBinderService =
displayManager.new BinderService();
@@ -3847,9 +3934,13 @@ public class DisplayManagerServiceTest {
STANDARD_DISPLAY_EVENTS);
waitForIdleHandler(handler);
- displayManagerBinderService.setDisplayTopology(new DisplayTopology());
+ var topology = initDisplayTopology(displayManager, displayManagerBinderService, callback,
+ handler, /*shouldEmitTopologyChangeEvent=*/ false);
+ callback.clear();
+ callback.expectsEvent(TOPOLOGY_CHANGED_EVENT); // should not happen
+ displayManagerBinderService.setDisplayTopology(topology);
+ callback.waitForNonExpectedEvent(); // checks that event did not happen
waitForIdleHandler(handler);
-
assertThat(callback.receivedEvents()).isEmpty();
}
@@ -4527,6 +4618,7 @@ public class DisplayManagerServiceTest {
new float[0], new int[0]);
}
displayDeviceInfo.name = "" + displayType;
+ displayDeviceInfo.uniqueId = "uniqueId" + mUniqueIdCount++;
displayDeviceInfo.modeId = 1;
displayDeviceInfo.type = displayType;
displayDeviceInfo.renderFrameRate = displayDeviceInfo.supportedModes[0].getRefreshRate();
@@ -4599,6 +4691,42 @@ public class DisplayManagerServiceTest {
}
}
+ private DisplayTopology initDisplayTopology(DisplayManagerService displayManager,
+ DisplayManagerService.BinderService displayManagerBinderService,
+ FakeDisplayManagerCallback callback,
+ Handler handler, boolean shouldEmitTopologyChangeEvent) {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 1);
+ callback.expectsEvent(TOPOLOGY_CHANGED_EVENT);
+ FakeDisplayDevice displayDevice0 =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ int displayId0 = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService,
+ displayDevice0);
+ if (shouldEmitTopologyChangeEvent) {
+ callback.waitForExpectedEvent();
+ } else {
+ callback.waitForNonExpectedEvent();
+ }
+ callback.clear();
+
+ callback.expectsEvent(TOPOLOGY_CHANGED_EVENT);
+ FakeDisplayDevice displayDevice1 = createFakeDisplayDevice(displayManager,
+ new float[]{60f}, Display.TYPE_OVERLAY);
+ int displayId1 = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService,
+ displayDevice1);
+ waitForIdleHandler(handler);
+ if (shouldEmitTopologyChangeEvent) {
+ callback.waitForExpectedEvent();
+ } else {
+ callback.waitForNonExpectedEvent();
+ }
+
+ var topology = new DisplayTopology();
+ topology.addDisplay(displayId0, 2048, 800);
+ topology.addDisplay(displayId1, 1920, 1080);
+ return topology;
+ }
+
private static class FakeDisplayManagerCallback extends IDisplayManagerCallback.Stub
implements DisplayManagerInternal.DisplayGroupListener {
int mDisplayId;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
index b09947aaf2ad..04dff1d36495 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
@@ -18,6 +18,7 @@ package com.android.server.display
import android.hardware.display.DisplayTopology
import android.hardware.display.DisplayTopology.pxToDp
+import android.util.SparseArray
import android.view.Display
import android.view.DisplayInfo
import com.google.common.truth.Truth.assertThat
@@ -37,9 +38,11 @@ class DisplayTopologyCoordinatorTest {
private val displayInfo = DisplayInfo()
private val topologyChangeExecutor = Runnable::run
+ private val mockTopologyStore = mock<DisplayTopologyStore>()
private val mockTopology = mock<DisplayTopology>()
private val mockTopologyCopy = mock<DisplayTopology>()
private val mockIsExtendedDisplayEnabled = mock<() -> Boolean>()
+ private val mockTopologySavedCallback = mock<() -> Unit>()
private val mockTopologyChangedCallback = mock<(DisplayTopology) -> Unit>()
@Before
@@ -51,11 +54,17 @@ class DisplayTopologyCoordinatorTest {
val injector = object : DisplayTopologyCoordinator.Injector() {
override fun getTopology() = mockTopology
+ override fun createTopologyStore(
+ displayIdToUniqueId: SparseArray<String>,
+ uniqueIdToDisplayId: MutableMap<String, Int>
+ ) =
+ mockTopologyStore
}
whenever(mockIsExtendedDisplayEnabled()).thenReturn(true)
whenever(mockTopology.copy()).thenReturn(mockTopologyCopy)
coordinator = DisplayTopologyCoordinator(injector, mockIsExtendedDisplayEnabled,
- mockTopologyChangedCallback, topologyChangeExecutor, DisplayManagerService.SyncRoot())
+ mockTopologyChangedCallback, topologyChangeExecutor, DisplayManagerService.SyncRoot(),
+ mockTopologySavedCallback)
}
@Test
@@ -66,6 +75,7 @@ class DisplayTopologyCoordinatorTest {
val heightDp = pxToDp(displayInfo.logicalHeight.toFloat(), displayInfo.logicalDensityDpi)
verify(mockTopology).addDisplay(displayInfo.displayId, widthDp, heightDp)
verify(mockTopologyChangedCallback).invoke(mockTopologyCopy)
+ verify(mockTopologyStore).restoreTopology(mockTopology)
}
@Test
@@ -76,6 +86,7 @@ class DisplayTopologyCoordinatorTest {
verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat())
verify(mockTopologyChangedCallback, never()).invoke(any())
+ verify(mockTopologyStore, never()).restoreTopology(any())
}
@Test
@@ -86,6 +97,7 @@ class DisplayTopologyCoordinatorTest {
verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat())
verify(mockTopologyChangedCallback, never()).invoke(any())
+ verify(mockTopologyStore, never()).restoreTopology(any())
}
@Test
@@ -115,6 +127,7 @@ class DisplayTopologyCoordinatorTest {
coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY)
verify(mockTopologyChangedCallback).invoke(mockTopologyCopy)
+ verify(mockTopologyStore).restoreTopology(mockTopology)
}
@Test
@@ -124,6 +137,7 @@ class DisplayTopologyCoordinatorTest {
coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY)
verify(mockTopologyChangedCallback, never()).invoke(any())
+ verify(mockTopologyStore, never()).restoreTopology(any())
}
@Test
@@ -136,10 +150,12 @@ class DisplayTopologyCoordinatorTest {
val topology = mock<DisplayTopology>()
val topologyCopy = mock<DisplayTopology>()
whenever(topology.copy()).thenReturn(topologyCopy)
-
+ whenever(mockTopologyStore.saveTopology(topology)).thenReturn(true)
coordinator.topology = topology
verify(topology).normalize()
verify(mockTopologyChangedCallback).invoke(topologyCopy)
+ verify(mockTopologyStore).saveTopology(topology)
+ verify(mockTopologySavedCallback).invoke()
}
} \ No newline at end of file
diff --git a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
index 439243e85e75..6e9a28f344e3 100644
--- a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
+++ b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
@@ -139,8 +139,8 @@ public class AudioManagerRouteControllerTest {
mMockAudioManager,
Looper.getMainLooper(),
mMediaAudioProductStrategy,
- btAdapter,
- mOnDeviceRouteChangedListener);
+ btAdapter);
+ mControllerUnderTest.registerRouteChangeListener(mOnDeviceRouteChangedListener);
mControllerUnderTest.start(UserHandle.CURRENT_OR_SELF);
ArgumentCaptor<AudioDeviceCallback> deviceCallbackCaptor =
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/SystemBackupAgentTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/SystemBackupAgentTest.java
index 7e179095d99b..86bf203771ba 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/SystemBackupAgentTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/SystemBackupAgentTest.java
@@ -93,7 +93,8 @@ public class SystemBackupAgentTest {
"app_locales",
"app_gender",
"companion",
- "system_gender");
+ "system_gender",
+ "display");
}
@Test
@@ -118,7 +119,8 @@ public class SystemBackupAgentTest {
"app_locales",
"app_gender",
"companion",
- "system_gender");
+ "system_gender",
+ "display");
}
@Test
@@ -136,7 +138,8 @@ public class SystemBackupAgentTest {
"app_locales",
"companion",
"app_gender",
- "system_gender");
+ "system_gender",
+ "display");
}
@Test
@@ -158,7 +161,8 @@ public class SystemBackupAgentTest {
"shortcut_manager",
"companion",
"app_gender",
- "system_gender");
+ "system_gender",
+ "display");
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 8a10040f986f..8dc657ed75a6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -49,6 +49,7 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.KeyguardManager;
+import android.app.PropertyInvalidatedCache;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
@@ -181,8 +182,8 @@ public final class UserManagerServiceTest {
MockitoAnnotations.initMocks(this);
mSpiedContext = spy(mRealContext);
- // Called when WatchedUserStates is constructed
- doNothing().when(() -> UserManager.invalidateIsUserUnlockedCache());
+ // Disable binder caches in this process.
+ PropertyInvalidatedCache.disableForTestMode();
// Called when creating new users
when(mDeviceStorageMonitorInternal.isMemoryLow()).thenReturn(false);
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 1cf655675a30..f583d4e67cc2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -144,6 +144,8 @@ public class WallpaperManagerServiceTests {
private static ComponentName sImageWallpaperComponentName;
private static ComponentName sDefaultWallpaperComponent;
+ private static ComponentName sFallbackWallpaperComponentName;
+
private IPackageManager mIpm = AppGlobals.getPackageManager();
@Mock
@@ -195,6 +197,8 @@ public class WallpaperManagerServiceTests {
sContext.getResources().getString(R.string.image_wallpaper_component));
// Mock default wallpaper as image wallpaper if there is no pre-defined default wallpaper.
sDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(sContext);
+ sFallbackWallpaperComponentName = ComponentName.unflattenFromString(
+ sContext.getResources().getString(R.string.fallback_wallpaper_component));
if (sDefaultWallpaperComponent == null) {
sDefaultWallpaperComponent = sImageWallpaperComponentName;
@@ -205,6 +209,9 @@ public class WallpaperManagerServiceTests {
}
sContext.addMockService(sImageWallpaperComponentName, sWallpaperService);
+ if (sFallbackWallpaperComponentName != null) {
+ sContext.addMockService(sFallbackWallpaperComponentName, sWallpaperService);
+ }
}
@AfterClass
@@ -216,6 +223,7 @@ public class WallpaperManagerServiceTests {
LocalServices.removeServiceForTest(WindowManagerInternal.class);
sImageWallpaperComponentName = null;
sDefaultWallpaperComponent = null;
+ sFallbackWallpaperComponentName = null;
reset(sContext);
}
@@ -306,6 +314,7 @@ public class WallpaperManagerServiceTests {
* Tests that internal basic data should be correct after boot up.
*/
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
public void testDataCorrectAfterBoot() {
mService.switchUser(USER_SYSTEM, null);
@@ -869,6 +878,22 @@ public class WallpaperManagerServiceTests {
}
// Verify a secondary display removed ended
+ // Test fallback wallpaper after enabling connected display supports.
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+ public void testFallbackWallpaperForConnectedDisplays() {
+ final WallpaperData fallbackData = mService.mFallbackWallpaper;
+
+ assertWithMessage(
+ "The connected wallpaper component should be fallback wallpaper component from "
+ + "config file.")
+ .that(fallbackData.connection.mWallpaper.getComponent())
+ .isEqualTo(sFallbackWallpaperComponentName);
+ assertWithMessage("Fallback wallpaper should support both lock & system.")
+ .that(fallbackData.mWhich)
+ .isEqualTo(FLAG_LOCK | FLAG_SYSTEM);
+ }
+
// Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
// non-current user must not bind to wallpaper service.
private void verifyNoConnectionBeforeLastUser(int lastUserId) {
@@ -893,11 +918,11 @@ public class WallpaperManagerServiceTests {
final WallpaperData lastLockData = mService.mLastLockWallpaper;
assertWithMessage("Last wallpaper must not be null").that(lastLockData).isNotNull();
assertWithMessage("Last wallpaper component must be equals.")
- .that(expectedComponent)
- .isEqualTo(lastLockData.getComponent());
+ .that(lastLockData.getComponent())
+ .isEqualTo(expectedComponent);
assertWithMessage("The user id in last wallpaper should be the last switched user")
- .that(lastUserId)
- .isEqualTo(lastLockData.userId);
+ .that(lastLockData.userId)
+ .isEqualTo(lastUserId);
assertWithMessage("Must exist user data connection on last wallpaper data")
.that(lastLockData.connection)
.isNotNull();
diff --git a/services/tests/security/intrusiondetection/AndroidTest.xml b/services/tests/security/intrusiondetection/AndroidTest.xml
index 0d211585958a..a0e846b90f03 100644
--- a/services/tests/security/intrusiondetection/AndroidTest.xml
+++ b/services/tests/security/intrusiondetection/AndroidTest.xml
@@ -25,7 +25,7 @@
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="setprop intrusiondetection_service_name com.android.coretests.apps.testapp/.TestLoggingService" />
+ <option name="run-command" value="setprop debug.intrusiondetection_package_name com.android.coretests.apps.testapp/.TestLoggingService" />
</target_preparer>
<option name="test-tag" value="IntrusionDetectionServiceTests" />
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
index affcfc14034e..379079a0018c 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
@@ -300,6 +300,22 @@ public class MediaProjectionStopControllerTest {
}
@Test
+ public void isStopReasonCallEnd_stopReasonCallEnd_returnsTrue() {
+ boolean result =
+ mStopController.isStopReasonCallEnd(
+ MediaProjectionStopController.STOP_REASON_CALL_END);
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void isStopReasonCallEnd_stopReasonKeyguard_returnsFalse() {
+ boolean result =
+ mStopController.isStopReasonCallEnd(
+ MediaProjectionStopController.STOP_REASON_KEYGUARD);
+ assertThat(result).isFalse();
+ }
+
+ @Test
@EnableFlags(
android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
public void testKeyguardLockedStateChanged_unlocked() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
index 322c3571734f..5a89db143b34 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -100,7 +100,7 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
assertEquals(99, mService.mMaxUpdatesPerInterval);
}
- public void testRoot() throws Exception {
+ public void disabled_testRoot() throws Exception {
mService.mMaxUpdatesPerInterval = 99;
mInjectedCallingUid = Process.ROOT_UID;
@@ -128,7 +128,7 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
assertEquals(1, mService.mMaxUpdatesPerInterval);
}
- public void testResetThrottling() throws Exception {
+ public void disabled_testResetThrottling() throws Exception {
prepareCrossProfileDataSet();
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -149,7 +149,7 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
});
}
- public void testResetThrottling_user_not_running() throws Exception {
+ public void disabled_testResetThrottling_user_not_running() throws Exception {
prepareCrossProfileDataSet();
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -177,7 +177,7 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
});
}
- public void testResetThrottling_user_running() throws Exception {
+ public void disabled_testResetThrottling_user_running() throws Exception {
prepareCrossProfileDataSet();
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -201,7 +201,7 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
});
}
- public void testResetAllThrottling() throws Exception {
+ public void disabled_testResetAllThrottling() throws Exception {
prepareCrossProfileDataSet();
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -262,7 +262,7 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
"Launcher: ComponentInfo{com.android.test.1/name}");
}
- public void testUnloadUser() throws Exception {
+ public void disabled_testUnloadUser() throws Exception {
prepareCrossProfileDataSet();
assertNotNull(mService.getShortcutsForTest().get(USER_11));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
index 9b02a3abf14b..1c0ee09ccc6f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
@@ -84,7 +84,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
makeCallerForeground();
}
- public void testGetParentOrSelfUserId() {
+ public void disabled_testGetParentOrSelfUserId() {
assertEquals(USER_10, mService.getParentOrSelfUserId(USER_10));
assertEquals(USER_11, mService.getParentOrSelfUserId(USER_11));
assertEquals(USER_12, mService.getParentOrSelfUserId(USER_12));
@@ -222,7 +222,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
actualIntent.getFlags());
}
- public void testNotForeground() {
+ public void disabled_testNotForeground() {
setDefaultLauncher(USER_10, LAUNCHER_1);
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -336,7 +336,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
checkRequestPinShortcut(makeResultIntent());
}
- public void testRequestPinShortcut_explicitTargetActivity() {
+ public void disabled_testRequestPinShortcut_explicitTargetActivity() {
setDefaultLauncher(USER_10, LAUNCHER_1);
setDefaultLauncher(USER_11, LAUNCHER_2);
@@ -475,7 +475,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
}
- public void testRequestPinShortcut_dynamicExists() {
+ public void disabled_testRequestPinShortcut_dynamicExists() {
setDefaultLauncher(USER_10, LAUNCHER_1);
final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
@@ -590,7 +590,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
});
}
- public void testRequestPinShortcut_dynamicExists_alreadyPinned() {
+ public void disabled_testRequestPinShortcut_dynamicExists_alreadyPinned() {
setDefaultLauncher(USER_10, LAUNCHER_1);
final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
@@ -848,7 +848,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
});
}
- public void testRequestPinShortcut_dynamicExists_alreadyPinnedByAnother() {
+ public void disabled_testRequestPinShortcut_dynamicExists_alreadyPinnedByAnother() {
// Initially all launchers have the shortcut permission, until we call setDefaultLauncher().
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -1041,7 +1041,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
/**
* When trying to pin an existing shortcut, the new fields shouldn't override existing fields.
*/
- public void testRequestPinShortcut_dynamicExists_titleWontChange() {
+ public void disabled_testRequestPinShortcut_dynamicExists_titleWontChange() {
setDefaultLauncher(USER_10, LAUNCHER_1);
final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
@@ -1173,7 +1173,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
* The dynamic shortcut existed, but before accepting(), it's removed. Because the request
* has a partial shortcut, accept() should fail.
*/
- public void testRequestPinShortcut_dynamicExists_thenRemoved_error() {
+ public void disabled_testRequestPinShortcut_dynamicExists_thenRemoved_error() {
setDefaultLauncher(USER_10, LAUNCHER_1);
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -1231,7 +1231,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
* The dynamic shortcut existed, but before accepting(), it's removed. Because the request
* has all the mandatory fields, we can go ahead and still publish it.
*/
- public void testRequestPinShortcut_dynamicExists_thenRemoved_okay() {
+ public void disabled_testRequestPinShortcut_dynamicExists_thenRemoved_okay() {
setDefaultLauncher(USER_10, LAUNCHER_1);
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -1404,7 +1404,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
* The dynamic shortcut existed, but before accepting(), it's removed. Because the request
* has a partial shortcut, accept() should fail.
*/
- public void testRequestPinShortcut_dynamicExists_thenDisabled_error() {
+ public void disabled_testRequestPinShortcut_dynamicExists_thenDisabled_error() {
setDefaultLauncher(USER_10, LAUNCHER_1);
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
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 43302264964e..a8fcadd9dd74 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -51,6 +51,7 @@ import static android.app.NotificationChannel.RECS_ID;
import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
+import static android.app.NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED;
import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
@@ -123,7 +124,9 @@ import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICAT
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_LOCKDOWN;
@@ -163,6 +166,7 @@ import static junit.framework.Assert.fail;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyLong;
@@ -253,7 +257,6 @@ import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.session.MediaSession;
import android.net.ConnectivityManager;
-import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.Uri;
import android.os.Binder;
@@ -361,7 +364,6 @@ import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
-import org.mockito.ArgumentMatchers;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -369,6 +371,9 @@ import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -384,9 +389,6 @@ import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@RunWithLooper
@@ -571,6 +573,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Mock
NotificationAttentionHelper mAttentionHelper;
+ @Mock
+ NetworkCapabilities mWifiNetworkCapabilities;
+
private NotificationManagerService.WorkerHandler mWorkerHandler;
private class TestableToastCallback extends ITransientNotification.Stub {
@@ -598,7 +603,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return FlagsParameterization.allCombinationsOf();
+ return FlagsParameterization.allCombinationsOf(
+ FLAG_NOTIFICATION_CLASSIFICATION);
}
public NotificationManagerServiceTest(FlagsParameterization flags) {
@@ -771,7 +777,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mActivityIntentImmutable = spy(PendingIntent.getActivity(mContext, 0,
new Intent().setPackage(mPkg), FLAG_IMMUTABLE));
- when(mConnectivityManager.getActiveNetwork()).thenReturn(null);
+ when(mWifiNetworkCapabilities.hasTransport(eq(NetworkCapabilities.TRANSPORT_WIFI)))
+ .thenReturn(true);
+ when(mWifiNetworkCapabilities
+ .hasCapability(eq(NetworkCapabilities.NET_CAPABILITY_VALIDATED)))
+ .thenReturn(true);
+ when(mWifiNetworkCapabilities
+ .hasCapability(eq(NetworkCapabilities.NET_CAPABILITY_TRUSTED)))
+ .thenReturn(true);
initNMS();
}
@@ -10880,6 +10893,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Bundle signals = new Bundle();
signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
signals.putInt(KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
+ signals.putInt(KEY_TYPE, TYPE_PROMOTION);
Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals,
"", r.getUser().getIdentifier());
@@ -11404,8 +11418,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mContext).sendBroadcastAsUser(eqIntent(expected), eq(UserHandle.of(mUserId)));
}
+ private static Intent isIntentWithAction(String wantedAction) {
+ return argThat(
+ intent -> intent != null && wantedAction.equals(intent.getAction())
+ );
+ }
+
private static Intent eqIntent(Intent wanted) {
- return ArgumentMatchers.argThat(
+ return argThat(
new ArgumentMatcher<Intent>() {
@Override
public boolean matches(Intent argument) {
@@ -14391,13 +14411,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testMakeRankingUpdate_clearsHasSensitiveContentIfConnectedToWifi() {
mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS,
FLAG_REDACT_SENSITIVE_CONTENT_NOTIFICATIONS_ON_LOCKSCREEN);
- when(mConnectivityManager.getActiveNetwork()).thenReturn(mock(Network.class));
- when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(
- new NetworkCapabilities.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .build()
- );
- mService.updateWifiConnectionState();
+ mService.updateWifiConnectionState(mWifiNetworkCapabilities);
when(mListeners.hasSensitiveContent(any())).thenReturn(true);
NotificationRecord pkgA = new NotificationRecord(mContext,
generateSbn("a", 1000, 9, 0), mTestNotificationChannel);
@@ -14416,13 +14430,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testMakeRankingUpdate_doesntClearHasSensitiveContentIfNotConnectedToWifi() {
mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS,
FLAG_REDACT_SENSITIVE_CONTENT_NOTIFICATIONS_ON_LOCKSCREEN);
- when(mConnectivityManager.getActiveNetwork()).thenReturn(mock(Network.class));
- when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(
- new NetworkCapabilities.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .build()
- );
- mService.updateWifiConnectionState();
+ mService.updateWifiConnectionState(mock(NetworkCapabilities.class));
when(mListeners.hasSensitiveContent(any())).thenReturn(true);
NotificationRecord record = getSensitiveNotificationRecord();
mService.addNotification(record);
@@ -14440,13 +14448,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testMakeRankingUpdate_doesntClearHasSensitiveContentIfNotSysUi() {
mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_CONTENT_NOTIFICATIONS_ON_LOCKSCREEN);
- when(mConnectivityManager.getActiveNetwork()).thenReturn(mock(Network.class));
- when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(
- new NetworkCapabilities.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .build()
- );
- mService.updateWifiConnectionState();
+ mService.updateWifiConnectionState(mWifiNetworkCapabilities);
when(mListeners.hasSensitiveContent(any())).thenReturn(true);
NotificationRecord record = getSensitiveNotificationRecord();
mService.addNotification(record);
@@ -14463,13 +14465,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testMakeRankingUpdate_doesntClearHasSensitiveContentIfFlagDisabled() {
mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_CONTENT_NOTIFICATIONS_ON_LOCKSCREEN);
- when(mConnectivityManager.getActiveNetwork()).thenReturn(mock(Network.class));
- when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(
- new NetworkCapabilities.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .build()
- );
- mService.updateWifiConnectionState();
+ mService.updateWifiConnectionState(mWifiNetworkCapabilities);
when(mListeners.hasSensitiveContent(any())).thenReturn(true);
NotificationRecord record = getSensitiveNotificationRecord();
mService.addNotification(record);
@@ -17506,6 +17502,66 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags({Flags.FLAG_NM_BINDER_PERF_THROTTLE_EFFECTS_SUPPRESSOR_BROADCAST,
+ Flags.FLAG_NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS})
+ public void requestHintsFromListener_changingEffectsButNotSuppressor_noBroadcast()
+ throws Exception {
+ // Note that NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS is not strictly necessary; however each
+ // path will do slightly different calls so we force one of them to simplify the test.
+ when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+ INotificationListener token = mock(INotificationListener.class);
+ mService.isSystemUid = true;
+
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_CALL_EFFECTS);
+ mTestableLooper.moveTimeForward(500); // more than ZEN_BROADCAST_DELAY
+ waitForIdle();
+
+ verify(mContext, times(1)).sendBroadcastMultiplePermissions(
+ isIntentWithAction(ACTION_EFFECTS_SUPPRESSOR_CHANGED), any(), any(), any());
+
+ // Same suppressor suppresses something else.
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+ mTestableLooper.moveTimeForward(500); // more than ZEN_BROADCAST_DELAY
+ waitForIdle();
+
+ // Still 1 total calls (the previous one).
+ verify(mContext, times(1)).sendBroadcastMultiplePermissions(
+ isIntentWithAction(ACTION_EFFECTS_SUPPRESSOR_CHANGED), any(), any(), any());
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_NM_BINDER_PERF_THROTTLE_EFFECTS_SUPPRESSOR_BROADCAST,
+ Flags.FLAG_NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS})
+ public void requestHintsFromListener_changingSuppressor_throttlesBroadcast() throws Exception {
+ // Note that NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS is not strictly necessary; however each
+ // path will do slightly different calls so we force one of them to simplify the test.
+ when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+ INotificationListener token = mock(INotificationListener.class);
+ mService.isSystemUid = true;
+
+ // Several updates in quick succession.
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_CALL_EFFECTS);
+ mBinderService.clearRequestedListenerHints(token);
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+ mBinderService.clearRequestedListenerHints(token);
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_CALL_EFFECTS);
+ mBinderService.clearRequestedListenerHints(token);
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+
+ // No broadcasts yet!
+ verify(mContext, never()).sendBroadcastMultiplePermissions(any(), any(), any(), any());
+
+ mTestableLooper.moveTimeForward(500); // more than ZEN_BROADCAST_DELAY
+ waitForIdle();
+
+ // Only one broadcast after idle time.
+ verify(mContext, times(1)).sendBroadcastMultiplePermissions(
+ isIntentWithAction(ACTION_EFFECTS_SUPPRESSOR_CHANGED), any(), any(), any());
+ }
+
+ @Test
@EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
public void testApplyAdjustment_keyType_validType() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index ff8b6d3c1962..08eb1451bd6f 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -23,6 +23,8 @@ import static android.view.KeyEvent.KEYCODE_POWER;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.hardware.input.Flags.FLAG_ABORT_SLOW_MULTI_PRESS;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -35,9 +37,13 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
import android.os.SystemClock;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.KeyEvent;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import java.util.concurrent.BlockingQueue;
@@ -52,6 +58,8 @@ import java.util.concurrent.TimeUnit;
* atest WmTests:SingleKeyGestureTests
*/
public class SingleKeyGestureTests {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private SingleKeyGestureDetector mDetector;
private int mMaxMultiPressCount = 3;
@@ -260,6 +268,44 @@ public class SingleKeyGestureTests {
}
@Test
+ @EnableFlags(FLAG_ABORT_SLOW_MULTI_PRESS)
+ public void testMultipress_noLongPressBehavior_longPressCancelsMultiPress()
+ throws InterruptedException {
+ mLongPressOnPowerBehavior = false;
+
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, mLongPressTime /* pressTime */);
+
+ assertFalse(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ @EnableFlags(FLAG_ABORT_SLOW_MULTI_PRESS)
+ public void testMultipress_noVeryLongPressBehavior_veryLongPressCancelsMultiPress()
+ throws InterruptedException {
+ mLongPressOnPowerBehavior = false;
+ mVeryLongPressOnPowerBehavior = false;
+
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, mVeryLongPressTime /* pressTime */);
+
+ assertFalse(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ @DisableFlags(FLAG_ABORT_SLOW_MULTI_PRESS)
+ public void testMultipress_flagDisabled_noLongPressBehavior_longPressDoesNotCancelMultiPress()
+ throws InterruptedException {
+ mLongPressOnPowerBehavior = false;
+ mExpectedMultiPressCount = 2;
+
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, mLongPressTime /* pressTime */);
+
+ assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
public void testMultiPress() throws InterruptedException {
// Double presses.
mExpectedMultiPressCount = 2;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d3f98d1a1d70..0b3d720bf52a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3219,7 +3219,6 @@ public class CarrierConfigManager {
* The roaming indicator will be shown if this is {@code true} and will not be shown if this is
* {@code false}.
*/
- @FlaggedApi(Flags.FLAG_HIDE_ROAMING_ICON)
public static final String KEY_SHOW_ROAMING_INDICATOR_BOOL = "show_roaming_indicator_bool";
/**
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index 2d650ab20802..bb9e7065b154 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -326,6 +326,13 @@ public final class PreciseDisconnectCause {
@Deprecated
public static final int CDMA_ACCESS_BLOCKED = 1009;
+ /** Call was disconnected with cause code retry over volte. */
+ @FlaggedApi(Flags.FLAG_ADD_IMS_REDIAL_CODES_FOR_EMERGENCY_CALLS)
+ public static final int EMERGENCY_REDIAL_ON_IMS = 3001;
+ /** Call was disconnected with cause code retry over vowifi. */
+ @FlaggedApi(Flags.FLAG_ADD_IMS_REDIAL_CODES_FOR_EMERGENCY_CALLS)
+ public static final int EMERGENCY_REDIAL_ON_VOWIFI = 3002;
+
/* OEM specific error codes. To be used by OEMs when they don't want to
reveal error code which would be replaced by ERROR_UNSPECIFIED */
public static final int OEM_CAUSE_1 = 0xf001;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index da4165553e57..6d32303fb13b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2447,6 +2447,7 @@ public class TelephonyManager {
* as follows:
*
* <ul>
+ * <li>If the device is running Android 25Q2 or later, then null is returned.</li>
* <li>If the calling app's target SDK is API level 28 or lower and the app has the
* READ_PHONE_STATE permission then null is returned.</li>
* <li>If the calling app's target SDK is API level 28 or lower and the app does not have
@@ -2455,7 +2456,8 @@ public class TelephonyManager {
* </ul>
*
* @deprecated Legacy CDMA is unsupported.
- * @throws UnsupportedOperationException If the device does not have
+ * @throws UnsupportedOperationException If the device is running
+ * Android 25Q1 or earlier and does not have
* {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
*/
@FlaggedApi(Flags.FLAG_DEPRECATE_CDMA)
diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java
index 9e029a8d5e57..72b1780ceb06 100644
--- a/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java
+++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java
@@ -33,6 +33,7 @@ import org.junit.runners.JUnit4;
import perfetto.protos.ProtologCommon;
import java.io.File;
+import java.io.IOException;
@Presubmit
@RunWith(JUnit4.class)
@@ -159,4 +160,14 @@ public class ProtoLogViewerConfigReaderTest {
loadViewerConfig();
unloadViewerConfig();
}
+
+ @Test
+ public void testMessageHashIsAvailableInFile() throws IOException {
+ Truth.assertThat(mConfig.messageHashIsAvailableInFile(1)).isTrue();
+ Truth.assertThat(mConfig.messageHashIsAvailableInFile(2)).isTrue();
+ Truth.assertThat(mConfig.messageHashIsAvailableInFile(3)).isTrue();
+ Truth.assertThat(mConfig.messageHashIsAvailableInFile(4)).isTrue();
+ Truth.assertThat(mConfig.messageHashIsAvailableInFile(5)).isTrue();
+ Truth.assertThat(mConfig.messageHashIsAvailableInFile(6)).isFalse();
+ }
}
diff --git a/tools/aapt2/tools/finalize_res.py b/tools/aapt2/tools/finalize_res.py
index 0e4d865bc890..059f3b2087cf 100755
--- a/tools/aapt2/tools/finalize_res.py
+++ b/tools/aapt2/tools/finalize_res.py
@@ -38,13 +38,22 @@ Usage: $ANDROID_BUILD_TOP/frameworks/base/tools/aapt2/tools/finalize_res.py \
import re
import sys
+import subprocess
+from collections import defaultdict
resTypes = ["attr", "id", "style", "string", "dimen", "color", "array", "drawable", "layout",
"anim", "animator", "interpolator", "mipmap", "integer", "transition", "raw", "bool",
"fraction"]
+NO_FLAG_MAGIC_CONSTANT = "no_flag"
+
+_aconfig_map = {}
+_not_finalized = defaultdict(list)
_type_ids = {}
_type = ""
+_finalized_flags = defaultdict(list)
+_non_finalized_flags = defaultdict(list)
+
_lowest_staging_first_id = 0x01FFFFFF
@@ -53,13 +62,53 @@ _lowest_staging_first_id = 0x01FFFFFF
prefixed with removed_. The IDs are assigned without holes starting from the last ID for that
type currently finalized in public-final.xml.
"""
-def finalize_item(raw):
- name = raw.group(1)
- if re.match(r'_*removed.+', name):
- return ""
+def finalize_item(comment_and_item):
+ print("Processing:\n" + comment_and_item)
+ name = re.search('<public name="(.+?)"',comment_and_item, flags=re.DOTALL).group(1)
+ if re.match('removed_.+', name):
+ # Remove it from <staging-public-group> in public-staging.xml
+ # Include it as is in <staging-public-group-final> in public-final.xml
+ # Don't assign an id in public-final.xml
+ return ("", comment_and_item, "")
+
+ comment = re.search(' *<!--.+?-->\n', comment_and_item, flags=re.DOTALL).group(0)
+
+ match = re.search('<!-- @FlaggedApi\((.+?)\)', comment, flags=re.DOTALL)
+ if match:
+ flag = match.group(1)
+ else:
+ flag = NO_FLAG_MAGIC_CONSTANT
+
+ if flag.startswith("\""):
+ # Flag is a string value, just remove "
+ flag = flag.replace("\"", "")
+ else:
+ # Flag is a java constant, convert to string value
+ flag = flag.replace(".Flags.FLAG_", ".").lower()
+
+ if flag not in _aconfig_map:
+ raise Exception("Unknown flag: " + flag)
+
+ # READ_ONLY-ENABLED is a magic string from printflags output below
+ if _aconfig_map[flag] != "READ_ONLY-ENABLED":
+ _non_finalized_flags[flag].append(name)
+ # Keep it as is in <staging-public-group> in public-staging.xml
+ # Include as magic constant "removed_" in <staging-public-group-final> in public-final.xml
+ # Don't assign an id in public-final.xml
+ return (comment_and_item, " <public name=\"removed_\" />\n", "")
+
+ _finalized_flags[flag].append(name)
+
id = _type_ids[_type]
_type_ids[_type] += 1
- return ' <public type="%s" name="%s" id="%s" />\n' % (_type, name, '0x{0:0{1}x}'.format(id, 8))
+
+ # Removes one indentation step to align the comment with the item outside the
+ comment = re.sub("^ ", "", comment, flags=re.MULTILINE)
+
+ # Remove from <staging-public-group> in public-staging.xml
+ # Include as is in <staging-public-group-final> in public-final.xml
+ # Assign an id in public-final.xml
+ return ("", comment_and_item, comment + ' <public type="%s" name="%s" id="%s" />\n' % (_type, name, '0x{0:0{1}x}'.format(id, 8)))
"""
@@ -72,10 +121,26 @@ def finalize_group(raw):
_type = raw.group(1)
id = int(raw.group(2), 16)
_type_ids[_type] = _type_ids.get(_type, id)
- (res, count) = re.subn(' {0,4}<public name="(.+?)" */>\n', finalize_item, raw.group(3))
- if count > 0:
- res = raw.group(0).replace("staging-public-group",
- "staging-public-group-final") + '\n' + res
+
+
+ all = re.findall(' *<!--.*?<public name=".+?" */>\n', raw.group(3), flags=re.DOTALL)
+ res = ""
+ group_matches = ""
+ for match in all:
+ (staging_group, final_group, final_id_assignment) = finalize_item(match)
+
+ if staging_group:
+ _not_finalized[_type].append(staging_group)
+
+ if final_group:
+ group_matches += final_group
+
+ if final_id_assignment:
+ res += final_id_assignment
+
+ # Only add it to final.xml if new ids were actually assigned
+ if res:
+ res = '<staging-public-group-final type="%s" first-id="%s">\n%s </staging-public-group-final>\n\n%s' % (_type, raw.group(2), group_matches, res)
_lowest_staging_first_id = min(id, _lowest_staging_first_id)
return res
@@ -88,6 +153,15 @@ def collect_ids(raw):
id = int(m.group(2), 16)
_type_ids[type] = max(id + 1, _type_ids.get(type, 0))
+# This is a hack and assumes this script is run from the top directory
+output=subprocess.run("printflags --format='{fully_qualified_name} {permission}-{state}'", shell=True, capture_output=True, encoding="utf-8", check=True)
+for line in output.stdout.splitlines():
+ parts = line.split()
+ key = parts[0]
+ value = parts[1]
+ _aconfig_map[key]=value
+
+_aconfig_map[NO_FLAG_MAGIC_CONSTANT]="READ_ONLY-DISABLED"
with open(sys.argv[1], "r+") as stagingFile:
with open(sys.argv[2], "r+") as finalFile:
@@ -132,10 +206,25 @@ with open(sys.argv[1], "r+") as stagingFile:
nextId = _lowest_staging_first_id - 0x00010000
for resType in resTypes:
stagingFile.write(' <staging-public-group type="%s" first-id="%s">\n'
- ' </staging-public-group>\n\n' %
- (resType, '0x{0:0{1}x}'.format(nextId, 8)))
+ % (resType, '0x{0:0{1}x}'.format(nextId, 8)))
+ for item in _not_finalized[resType]:
+ stagingFile.write(item)
+ stagingFile.write(' </staging-public-group>\n\n')
nextId -= 0x00010000
# Close the resources tag and truncate, since the file will be shorter than the previous
stagingFile.write("</resources>\n")
stagingFile.truncate()
+
+
+print("\nFlags that had resources that were NOT finalized:")
+for flag in sorted(_non_finalized_flags.keys()):
+ print(f" {flag}")
+ for value in _non_finalized_flags[flag]:
+ print(f" {value}")
+
+print("\nFlags that had resources that were finalized:")
+for flag in sorted(_finalized_flags.keys()):
+ print(f" {flag}")
+ for value in _finalized_flags[flag]:
+ print(f" {value}")