summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/Activity.java21
-rw-r--r--core/java/android/app/Notification.java2
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java19
-rw-r--r--core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java2
-rw-r--r--core/java/android/database/sqlite/SQLiteDiskIOException.java2
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java17
-rw-r--r--core/java/android/provider/Settings.java24
-rw-r--r--core/java/android/service/appprediction/AppPredictionService.java2
-rw-r--r--core/java/android/service/autofill/FillEventHistory.java2
-rw-r--r--core/java/android/service/resumeonreboot/OWNERS1
-rw-r--r--core/java/android/service/smartspace/SmartspaceService.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java9
-rw-r--r--core/java/android/view/WindowManager.java14
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java16
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java6
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl7
-rw-r--r--core/java/com/android/internal/view/RotationPolicy.java9
-rw-r--r--core/res/OWNERS4
-rw-r--r--core/res/res/layout/autofill_fill_dialog.xml2
-rw-r--r--core/res/res/layout/autofill_save.xml2
-rw-r--r--core/res/res/values-eu/strings.xml2
-rw-r--r--core/res/res/values-ky/strings.xml12
-rw-r--r--core/res/res/values-pt-rBR/strings.xml2
-rw-r--r--core/res/res/values-pt/strings.xml2
-rw-r--r--core/res/res/values-vi/strings.xml22
-rw-r--r--core/res/res/values/config.xml9
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java16
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java7
-rw-r--r--libs/WindowManager/Jetpack/window-extensions-release.aarbin34425 -> 37896 bytes
-rw-r--r--libs/WindowManager/Shell/res/drawable/caption_decor_title.xml22
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml8
-rw-r--r--libs/WindowManager/Shell/res/layout/caption_window_decor.xml56
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java34
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithmInterface.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithm.java)2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java266
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java227
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java105
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java96
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java112
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java35
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt23
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java2
-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/pip/phone/PipResizeGestureHandlerTest.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java4
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java4
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java4
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java19
-rw-r--r--packages/PackageInstaller/res/values-ky/strings.xml2
-rw-r--r--packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java23
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-ky/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/arrays.xml2
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java8
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java16
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java29
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/UpdatableListPreferenceDialogFragmentTest.java2
-rw-r--r--packages/SystemUI/Android.bp1
-rw-r--r--packages/SystemUI/AndroidManifest.xml17
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt12
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt27
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt46
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt56
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt2
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt44
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt18
-rw-r--r--packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt18
-rw-r--r--packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt78
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/SysuiTestTag.kt32
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt7
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt32
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt5
-rw-r--r--packages/SystemUI/plugin/Android.bp1
-rw-r--r--packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt24
-rw-r--r--packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java5
-rw-r--r--packages/SystemUI/proguard.flags32
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-sw540dp-port/dimens.xml24
-rw-r--r--packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml5
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml3
-rw-r--r--packages/SystemUI/res-product/values-de/strings.xml4
-rw-r--r--packages/SystemUI/res-product/values-fr/strings.xml2
-rw-r--r--packages/SystemUI/res-product/values-ka/strings.xml2
-rw-r--r--packages/SystemUI/res-product/values-pt-rBR/strings.xml4
-rw-r--r--packages/SystemUI/res-product/values-pt/strings.xml4
-rw-r--r--packages/SystemUI/res-product/values-uz/strings.xml2
-rw-r--r--packages/SystemUI/res/drawable/ic_media_explicit_indicator.xml26
-rw-r--r--packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml4
-rw-r--r--packages/SystemUI/res/layout/auth_credential_pattern_view.xml4
-rw-r--r--packages/SystemUI/res/layout/media_session_view.xml8
-rw-r--r--packages/SystemUI/res/layout/ongoing_privacy_chip.xml1
-rw-r--r--packages/SystemUI/res/values-af/strings.xml19
-rw-r--r--packages/SystemUI/res/values-am/strings.xml17
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml19
-rw-r--r--packages/SystemUI/res/values-as/strings.xml17
-rw-r--r--packages/SystemUI/res/values-az/strings.xml17
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml17
-rw-r--r--packages/SystemUI/res/values-be/strings.xml17
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml21
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml15
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml17
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml23
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml17
-rw-r--r--packages/SystemUI/res/values-da/strings.xml19
-rw-r--r--packages/SystemUI/res/values-de/strings.xml33
-rw-r--r--packages/SystemUI/res/values-el/strings.xml15
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml10
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml9
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml10
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml10
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml9
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml19
-rw-r--r--packages/SystemUI/res/values-es/strings.xml19
-rw-r--r--packages/SystemUI/res/values-et/strings.xml19
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml19
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml17
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml17
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml17
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml21
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml19
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml17
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml21
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml17
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml19
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml17
-rw-r--r--packages/SystemUI/res/values-in/strings.xml17
-rw-r--r--packages/SystemUI/res/values-is/strings.xml17
-rw-r--r--packages/SystemUI/res/values-it/strings.xml19
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml25
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml17
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml21
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml17
-rw-r--r--packages/SystemUI/res/values-km/strings.xml19
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml17
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml21
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml33
-rw-r--r--packages/SystemUI/res/values-land/dimens.xml4
-rw-r--r--packages/SystemUI/res/values-land/styles.xml10
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml17
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml15
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml17
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml15
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml15
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml17
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml17
-rw-r--r--packages/SystemUI/res/values-my/strings.xml17
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml17
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml17
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml19
-rw-r--r--packages/SystemUI/res/values-or/strings.xml21
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml17
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml19
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml17
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml13
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml17
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml17
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml17
-rw-r--r--packages/SystemUI/res/values-si/strings.xml17
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml23
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml19
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml19
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml17
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml15
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml19
-rw-r--r--packages/SystemUI/res/values-sw360dp/dimens.xml3
-rw-r--r--packages/SystemUI/res/values-sw392dp-land/dimens.xml21
-rw-r--r--packages/SystemUI/res/values-sw392dp/dimens.xml3
-rw-r--r--packages/SystemUI/res/values-sw410dp-land/dimens.xml21
-rw-r--r--packages/SystemUI/res/values-sw410dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/dimens.xml1
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/styles.xml8
-rw-r--r--packages/SystemUI/res/values-sw600dp-port/styles.xml8
-rw-r--r--packages/SystemUI/res/values-sw600dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw720dp-land/dimens.xml6
-rw-r--r--packages/SystemUI/res/values-sw720dp-land/styles.xml8
-rw-r--r--packages/SystemUI/res/values-sw720dp-port/styles.xml8
-rw-r--r--packages/SystemUI/res/values-sw720dp/dimens.xml3
-rw-r--r--packages/SystemUI/res/values-sw800dp/dimens.xml24
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml17
-rw-r--r--packages/SystemUI/res/values-te/strings.xml15
-rw-r--r--packages/SystemUI/res/values-th/strings.xml17
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml15
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml19
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml17
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml19
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml14
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml17
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml21
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml21
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml19
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml17
-rw-r--r--packages/SystemUI/res/values/config.xml10
-rw-r--r--packages/SystemUI/res/values/dimens.xml8
-rw-r--r--packages/SystemUI/res/values/strings.xml23
-rw-r--r--packages/SystemUI/res/values/styles.xml11
-rw-r--r--packages/SystemUI/res/xml/large_screen_shade_header.xml95
-rw-r--r--packages/SystemUI/res/xml/media_session_collapsed.xml20
-rw-r--r--packages/SystemUI/res/xml/media_session_expanded.xml20
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/hardware/InputDevice.kt39
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/hardware/InputManager.kt69
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java11
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java55
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt44
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java43
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java54
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt96
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt115
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractor.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt69
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java322
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt214
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedFrameLayout.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsModule.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java105
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ResolvedNetworkType.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt140
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt181
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt179
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt133
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt182
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoModeWifiDataSource.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/model/FakeWifiEventModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/telephony/ui/activity/SwitchToManagedProfileForCallActivity.kt81
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt39
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt92
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt344
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt289
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt66
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java125
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt327
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt50
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt141
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt136
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt64
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt71
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskIntentResolverTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java71
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt60
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java68
-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/notification/row/NotificationContentViewTest.kt84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt143
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt103
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt255
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt309
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java94
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt267
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt251
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt389
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt243
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt327
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt72
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt63
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/stylus/FixedCapacityBatteryState.kt25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt77
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt134
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java4
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt4
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt91
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt50
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldSingleThreadBg.kt (renamed from packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt)5
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt8
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt3
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt11
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt46
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt5
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java20
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java1
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java21
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java3
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java8
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java1
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java5
-rw-r--r--services/core/java/com/android/server/wm/Task.java10
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java8
-rw-r--r--services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java15
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java28
-rw-r--r--services/people/java/com/android/server/people/data/ContactsQueryHelper.java3
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java55
-rw-r--r--services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java28
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java21
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java5
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java1
482 files changed, 11824 insertions, 2694 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d24b677b1d72..03a97ba1d977 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -997,6 +997,8 @@ public class Activity extends ContextThemeWrapper
private ComponentCallbacksController mCallbacksController;
+ @Nullable private IVoiceInteractionManagerService mVoiceInteractionManagerService;
+
private final WindowControllerCallback mWindowControllerCallback =
new WindowControllerCallback() {
/**
@@ -1606,18 +1608,17 @@ public class Activity extends ContextThemeWrapper
private void notifyVoiceInteractionManagerServiceActivityEvent(
@VoiceInteractionSession.VoiceInteractionActivityEventType int type) {
-
- final IVoiceInteractionManagerService service =
- IVoiceInteractionManagerService.Stub.asInterface(
- ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
- if (service == null) {
- Log.w(TAG, "notifyVoiceInteractionManagerServiceActivityEvent: Can not get "
- + "VoiceInteractionManagerService");
- return;
+ if (mVoiceInteractionManagerService == null) {
+ mVoiceInteractionManagerService = IVoiceInteractionManagerService.Stub.asInterface(
+ ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+ if (mVoiceInteractionManagerService == null) {
+ Log.w(TAG, "notifyVoiceInteractionManagerServiceActivityEvent: Can not get "
+ + "VoiceInteractionManagerService");
+ return;
+ }
}
-
try {
- service.notifyActivityEventChanged(mToken, type);
+ mVoiceInteractionManagerService.notifyActivityEventChanged(mToken, type);
} catch (RemoteException e) {
// Empty
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 8b41aa4a7e7d..46d8481646c9 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7944,8 +7944,6 @@ public class Notification implements Parcelable
* @hide
*/
public MessagingStyle setShortcutIcon(@Nullable Icon conversationIcon) {
- // TODO(b/228941516): This icon should be downscaled to avoid using too much memory,
- // see reduceImageSizes.
mShortcutIcon = conversationIcon;
return this;
}
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index a51b9d3956df..27f9f54d9522 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -1406,6 +1406,17 @@ public class PropertyInvalidatedCache<Query, Result> {
}
/**
+ * Return the number of entries in the cache. This is used for testing and has package-only
+ * visibility.
+ * @hide
+ */
+ public int size() {
+ synchronized (mLock) {
+ return mCache.size();
+ }
+ }
+
+ /**
* Returns a list of caches alive at the current time.
*/
@GuardedBy("sGlobalLock")
@@ -1612,8 +1623,12 @@ public class PropertyInvalidatedCache<Query, Result> {
* @hide
*/
public static void onTrimMemory() {
- for (PropertyInvalidatedCache pic : getActiveCaches()) {
- pic.clear();
+ ArrayList<PropertyInvalidatedCache> activeCaches;
+ synchronized (sGlobalLock) {
+ activeCaches = getActiveCaches();
+ }
+ for (int i = 0; i < activeCaches.size(); i++) {
+ activeCaches.get(i).clear();
}
}
}
diff --git a/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java b/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java
index 41f2f9c28349..b86b97c76740 100644
--- a/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java
+++ b/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java
@@ -17,7 +17,7 @@
package android.database.sqlite;
/**
- * Thrown if the the bind or column parameter index is out of range
+ * Thrown if the bind or column parameter index is out of range.
*/
public class SQLiteBindOrColumnIndexOutOfRangeException extends SQLiteException {
public SQLiteBindOrColumnIndexOutOfRangeException() {}
diff --git a/core/java/android/database/sqlite/SQLiteDiskIOException.java b/core/java/android/database/sqlite/SQLiteDiskIOException.java
index 01b2069c23db..152d90a76ba6 100644
--- a/core/java/android/database/sqlite/SQLiteDiskIOException.java
+++ b/core/java/android/database/sqlite/SQLiteDiskIOException.java
@@ -17,7 +17,7 @@
package android.database.sqlite;
/**
- * An exception that indicates that an IO error occured while accessing the
+ * Indicates that an IO error occurred while accessing the
* SQLite database file.
*/
public class SQLiteDiskIOException extends SQLiteException {
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index ea6f624b77ee..4dc6e9310220 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -392,6 +392,23 @@ public final class CameraManager {
* except that it uses {@link java.util.concurrent.Executor} as an argument
* instead of {@link android.os.Handler}.</p>
*
+ * <p>Note: If the order between some availability callbacks matters, the implementation of the
+ * executor should handle those callbacks in the same thread to maintain the callbacks' order.
+ * Some examples are:</p>
+ *
+ * <ul>
+ *
+ * <li>{@link AvailabilityCallback#onCameraAvailable} and
+ * {@link AvailabilityCallback#onCameraUnavailable} of the same camera ID.</li>
+ *
+ * <li>{@link AvailabilityCallback#onCameraAvailable} or
+ * {@link AvailabilityCallback#onCameraUnavailable} of a logical multi-camera, and {@link
+ * AvailabilityCallback#onPhysicalCameraUnavailable} or
+ * {@link AvailabilityCallback#onPhysicalCameraAvailable} of its physical
+ * cameras.</li>
+ *
+ * </ul>
+ *
* @param executor The executor which will be used to invoke the callback.
* @param callback the new callback to send camera availability notices to
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b21187af1271..d8d0bb27e6d9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9233,6 +9233,14 @@ public final class Settings {
public static final int DOCK_SETUP_PROMPTED = 3;
/**
+ * Indicates that the user has started dock setup but never finished it.
+ * One of the possible states for {@link #DOCK_SETUP_STATE}.
+ *
+ * @hide
+ */
+ public static final int DOCK_SETUP_INCOMPLETE = 4;
+
+ /**
* Indicates that the user has completed dock setup.
* One of the possible states for {@link #DOCK_SETUP_STATE}.
*
@@ -9240,6 +9248,14 @@ public final class Settings {
*/
public static final int DOCK_SETUP_COMPLETED = 10;
+ /**
+ * Indicates that dock setup timed out before the user could complete it.
+ * One of the possible states for {@link #DOCK_SETUP_STATE}.
+ *
+ * @hide
+ */
+ public static final int DOCK_SETUP_TIMED_OUT = 11;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({
@@ -9247,7 +9263,9 @@ public final class Settings {
DOCK_SETUP_STARTED,
DOCK_SETUP_PAUSED,
DOCK_SETUP_PROMPTED,
- DOCK_SETUP_COMPLETED
+ DOCK_SETUP_INCOMPLETE,
+ DOCK_SETUP_COMPLETED,
+ DOCK_SETUP_TIMED_OUT
})
public @interface DockSetupState {
}
@@ -9820,9 +9838,11 @@ public final class Settings {
/**
* Whether or not a SFPS device is enabling the performant auth setting.
+ * The "_V2" suffix was added to re-introduce the default behavior for
+ * users. See b/265264294 fore more details.
* @hide
*/
- public static final String SFPS_PERFORMANT_AUTH_ENABLED = "sfps_performant_auth_enabled";
+ public static final String SFPS_PERFORMANT_AUTH_ENABLED = "sfps_performant_auth_enabled_v2";
/**
* Whether or not debugging is enabled.
diff --git a/core/java/android/service/appprediction/AppPredictionService.java b/core/java/android/service/appprediction/AppPredictionService.java
index 4f37cd91b11f..a2ffa5d34219 100644
--- a/core/java/android/service/appprediction/AppPredictionService.java
+++ b/core/java/android/service/appprediction/AppPredictionService.java
@@ -328,7 +328,7 @@ public abstract class AppPredictionService extends Service {
Slog.e(TAG, "Callback is null, likely the binder has died.");
return false;
}
- return mCallback.equals(callback);
+ return mCallback.asBinder().equals(callback.asBinder());
}
public void destroy() {
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index 0fb9f57f5f57..b0e847cd53f9 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -166,7 +166,7 @@ public final class FillEventHistory implements Parcelable {
}
/**
- * Description of an event that occured after the latest call to
+ * Description of an event that occurred after the latest call to
* {@link FillCallback#onSuccess(FillResponse)}.
*/
public static final class Event {
diff --git a/core/java/android/service/resumeonreboot/OWNERS b/core/java/android/service/resumeonreboot/OWNERS
index 721fbaf2d4ed..8ffb76a69923 100644
--- a/core/java/android/service/resumeonreboot/OWNERS
+++ b/core/java/android/service/resumeonreboot/OWNERS
@@ -1 +1,2 @@
+aveena@google.com
ejyzhang@google.com \ No newline at end of file
diff --git a/core/java/android/service/smartspace/SmartspaceService.java b/core/java/android/service/smartspace/SmartspaceService.java
index 3a148dffe6d6..b13a069116af 100644
--- a/core/java/android/service/smartspace/SmartspaceService.java
+++ b/core/java/android/service/smartspace/SmartspaceService.java
@@ -302,7 +302,7 @@ public abstract class SmartspaceService extends Service {
Slog.e(TAG, "Callback is null, likely the binder has died.");
return false;
}
- return mCallback.equals(callback);
+ return mCallback.asBinder().equals(callback.asBinder());
}
@Override
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8d52d001da2e..43bbcfb1e94a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -73,6 +73,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FIT_INSETS_CO
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -2766,7 +2767,7 @@ public final class ViewRootImpl implements ViewParent,
* TODO(b/260382739): Apply this to all windows.
*/
private static boolean shouldOptimizeMeasure(final WindowManager.LayoutParams lp) {
- return lp.type == TYPE_NOTIFICATION_SHADE;
+ return (lp.privateFlags & PRIVATE_FLAG_OPTIMIZE_MEASURE) != 0;
}
private Rect getWindowBoundsInsetSystemBars() {
@@ -5045,7 +5046,7 @@ public final class ViewRootImpl implements ViewParent,
}
void reportKeepClearAreasChanged() {
- if (!mHasPendingKeepClearAreaChange) {
+ if (!mHasPendingKeepClearAreaChange || mView == null) {
return;
}
mHasPendingKeepClearAreaChange = false;
@@ -8747,6 +8748,10 @@ public final class ViewRootImpl implements ViewParent,
mAdded = false;
AnimationHandler.removeRequestor(this);
}
+ if (mSyncBufferCallback != null) {
+ mSyncBufferCallback.onBufferReady(null);
+ mSyncBufferCallback = null;
+ }
WindowManagerGlobal.getInstance().doRemoveView(this);
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index abc49266bb19..a37c24499aff 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2580,6 +2580,15 @@ public interface WindowManager extends ViewManager {
public static final int PRIVATE_FLAG_SYSTEM_ERROR = 0x00000100;
/**
+ * Flag to indicate that the view hierarchy of the window can only be measured when
+ * necessary. If a window size can be known by the LayoutParams, we can use the size to
+ * relayout window, and we don't have to measure the view hierarchy before laying out the
+ * views. This reduces the chances to perform measure.
+ * {@hide}
+ */
+ public static final int PRIVATE_FLAG_OPTIMIZE_MEASURE = 0x00000200;
+
+ /**
* Flag that prevents the wallpaper behind the current window from receiving touch events.
*
* {@hide}
@@ -2781,6 +2790,7 @@ public interface WindowManager extends ViewManager {
PRIVATE_FLAG_NO_MOVE_ANIMATION,
PRIVATE_FLAG_COMPATIBLE_WINDOW,
PRIVATE_FLAG_SYSTEM_ERROR,
+ PRIVATE_FLAG_OPTIMIZE_MEASURE,
PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR,
PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT,
@@ -2841,6 +2851,10 @@ public interface WindowManager extends ViewManager {
equals = PRIVATE_FLAG_SYSTEM_ERROR,
name = "SYSTEM_ERROR"),
@ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_OPTIMIZE_MEASURE,
+ equals = PRIVATE_FLAG_OPTIMIZE_MEASURE,
+ name = "OPTIMIZE_MEASURE"),
+ @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
equals = PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
name = "DISABLE_WALLPAPER_TOUCH_EVENTS"),
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 1fcfe7dd5b6f..011232fe1915 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2953,12 +2953,24 @@ public class ChooserActivity extends ResolverActivity implements
private boolean shouldShowStickyContentPreviewNoOrientationCheck() {
return shouldShowTabs()
- && mMultiProfilePagerAdapter.getListAdapterForUserHandle(
- UserHandle.of(UserHandle.myUserId())).getCount() > 0
+ && (mMultiProfilePagerAdapter.getListAdapterForUserHandle(
+ UserHandle.of(UserHandle.myUserId())).getCount() > 0
+ || shouldShowContentPreviewWhenEmpty())
&& shouldShowContentPreview();
}
/**
+ * This method could be used to override the default behavior when we hide the preview area
+ * when the current tab doesn't have any items.
+ *
+ * @return true if we want to show the content preview area even if the tab for the current
+ * user is empty
+ */
+ protected boolean shouldShowContentPreviewWhenEmpty() {
+ return false;
+ }
+
+ /**
* @return true if we want to show the content preview area
*/
protected boolean shouldShowContentPreview() {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f8b764be582b..19e4ba405feb 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -209,7 +209,7 @@ public class ResolverActivity extends Activity implements
* <p>Can only be used if there is a work profile.
* <p>Possible values can be either {@link #PROFILE_PERSONAL} or {@link #PROFILE_WORK}.
*/
- static final String EXTRA_SELECTED_PROFILE =
+ protected static final String EXTRA_SELECTED_PROFILE =
"com.android.internal.app.ResolverActivity.EXTRA_SELECTED_PROFILE";
/**
@@ -224,8 +224,8 @@ public class ResolverActivity extends Activity implements
static final String EXTRA_CALLING_USER =
"com.android.internal.app.ResolverActivity.EXTRA_CALLING_USER";
- static final int PROFILE_PERSONAL = AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL;
- static final int PROFILE_WORK = AbstractMultiProfilePagerAdapter.PROFILE_WORK;
+ protected static final int PROFILE_PERSONAL = AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL;
+ protected static final int PROFILE_WORK = AbstractMultiProfilePagerAdapter.PROFILE_WORK;
private BroadcastReceiver mWorkProfileStateReceiver;
private UserHandle mHeaderCreatorUser;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 017bf3f3dd6c..04fc4a6fd81d 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -338,4 +338,11 @@ oneway interface IStatusBar
* @param leftOrTop indicates where the stage split is.
*/
void enterStageSplitFromRunningApp(boolean leftOrTop);
+
+ /**
+ * Shows the media output switcher dialog.
+ *
+ * @param packageName of the session for which the output switcher is shown.
+ */
+ void showMediaOutputSwitcher(String packageName);
}
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index 869da1ffcb6e..058c6ec4d13c 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -106,7 +106,9 @@ public final class RotationPolicy {
* Enables or disables rotation lock from the system UI toggle.
*/
public static void setRotationLock(Context context, final boolean enabled) {
- final int rotation = areAllRotationsAllowed(context) ? CURRENT_ROTATION : NATURAL_ROTATION;
+ final int rotation = areAllRotationsAllowed(context)
+ || useCurrentRotationOnRotationLockChange(context) ? CURRENT_ROTATION
+ : NATURAL_ROTATION;
setRotationLockAtAngle(context, enabled, rotation);
}
@@ -139,6 +141,11 @@ public final class RotationPolicy {
return context.getResources().getBoolean(R.bool.config_allowAllRotations);
}
+ private static boolean useCurrentRotationOnRotationLockChange(Context context) {
+ return context.getResources().getBoolean(
+ R.bool.config_useCurrentRotationOnRotationLockChange);
+ }
+
private static void setRotationLock(final boolean enabled, final int rotation) {
AsyncTask.execute(new Runnable() {
@Override
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 3c143668489d..8c041b20fc81 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -28,8 +28,8 @@ yamasani@google.com
# WindowManager team
# TODO(262451702): Move WindowManager configs out of config.xml in a separate file
-per-file core/res/res/values/config.xml = file:/services/core/java/com/android/server/wm/OWNERS
-per-file core/res/res/values/symbols.xml = file:/services/core/java/com/android/server/wm/OWNERS
+per-file res/values/config.xml = file:/services/core/java/com/android/server/wm/OWNERS
+per-file res/values/symbols.xml = file:/services/core/java/com/android/server/wm/OWNERS
# Resources finalization
per-file res/xml/public-staging.xml = file:/tools/aapt2/OWNERS
diff --git a/core/res/res/layout/autofill_fill_dialog.xml b/core/res/res/layout/autofill_fill_dialog.xml
index c382a6577857..2e65800d5abb 100644
--- a/core/res/res/layout/autofill_fill_dialog.xml
+++ b/core/res/res/layout/autofill_fill_dialog.xml
@@ -93,7 +93,7 @@
android:layout_height="36dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
- style="@style/AutofillHalfSheetOutlinedButton"
+ style="?android:attr/borderlessButtonStyle"
android:text="@string/autofill_save_no">
</Button>
diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml
index fd08241deb3a..3c0b789dbe6f 100644
--- a/core/res/res/layout/autofill_save.xml
+++ b/core/res/res/layout/autofill_save.xml
@@ -81,7 +81,7 @@
android:layout_height="36dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
- style="@style/AutofillHalfSheetOutlinedButton"
+ style="?android:attr/borderlessButtonStyle"
android:text="@string/autofill_save_no">
</Button>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index e60a96e3a4b7..ce02af221d0f 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -78,7 +78,7 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Deitzailearen identitatea zerbitzuaren balio lehenetsiak ez du murriztapenik ezartzen. Hurrengo deia: murriztapenik gabe."</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Zerbitzua ez da hornitu."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Ezin duzu aldatu deitzailearen identitatearen ezarpena."</string>
- <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ez dago mugikorreko datu-zerbitzurik"</string>
+ <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ez dago mugikorretarako datu-zerbitzurik"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Ezin da egin larrialdi-deirik"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ez dago ahots-deien zerbitzurik"</string>
<string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"Ez dago ahozko zerbitzurik eta ezin da egin larrialdi-deirik"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 3db4e9bded7d..27d932aac8fa 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -264,7 +264,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"Учак режими"</string>
<string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"Учак режими КҮЙҮК"</string>
<string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"Учак режими ӨЧҮК"</string>
- <string name="global_action_settings" msgid="4671878836947494217">"Жөндөөлөр"</string>
+ <string name="global_action_settings" msgid="4671878836947494217">"Параметрлер"</string>
<string name="global_action_assist" msgid="2517047220311505805">"Жардам"</string>
<string name="global_action_voice_assist" msgid="6655788068555086695">"Үн жардамчысы"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Бекем кулпулоо"</string>
@@ -631,7 +631,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Жүзүңүздүн үлгүсүн өчүрүү үчүн басып, жаңы үлгүнү кошуңуз"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Жүзүнөн таанып ачууну тууралоо"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Телефонуңузду карап туруп эле кулпусун ачып алыңыз"</string>
- <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Жүзүнөн таанып ачуу функциясын колдонуу үчүн Жөндөөлөр &gt; Купуялык бөлүмүнө өтүп, "<b>"Камераны колдонууну"</b>" күйгүзүңүз"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Жүзүнөн таанып ачуу функциясын колдонуу үчүн Параметрлер &gt; Купуялык бөлүмүнө өтүп, "<b>"Камераны колдонууну"</b>" күйгүзүңүз"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Кулпусун ачуунун көбүрөөк жолдорун жөндөңүз"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Манжа изин кошуу үчүн басыңыз"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Кулпуланган түзмөктү манжа изи менен ачуу"</string>
@@ -1624,7 +1624,7 @@
<string name="media_route_chooser_title" msgid="6646594924991269208">"Түзмөккө туташуу"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Тышкы экранга чыгаруу"</string>
<string name="media_route_chooser_searching" msgid="6119673534251329535">"Түзмөктөр изделүүдө..."</string>
- <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Жөндөөлөр"</string>
+ <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Параметрлер"</string>
<string name="media_route_controller_disconnect" msgid="7362617572732576959">"Ажыратуу"</string>
<string name="media_route_status_scanning" msgid="8045156315309594482">"Скандоодо..."</string>
<string name="media_route_status_connecting" msgid="5845597961412010540">"Туташууда..."</string>
@@ -1679,10 +1679,10 @@
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Ыкчам иштетесизби?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең 3 секунддай коё бербей басып туруңуз."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Атайын мүмкүнчүлүктөрдүн ыкчам баскычын иштетесизби?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Атайын мүмкүнчүлүктөр функциясын иштетүү үчүн үндү катуулатуу/акырындатуу баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nУчурдагы функциялар:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТандалган функцияларды өзгөртүү үчүн Жөндөөлөр &gt; Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Атайын мүмкүнчүлүктөр функциясын иштетүү үчүн үндү катуулатуу/акырындатуу баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nУчурдагы функциялар:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТандалган функцияларды өзгөртүү үчүн Параметрлер &gt; Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="2128323171922023762">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"<xliff:g id="SERVICE">%1$s</xliff:g> ыкчам баскычын иштетесизби?"</string>
- <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"<xliff:g id="SERVICE">%1$s</xliff:g> кызматын иштетүү үчүн үндү катуулатуу/акырындатуу баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nБаскычтардын ушул айкалышын башка функцияга дайындоо үчүн, Жөндөөлөр &gt; Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string>
+ <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"<xliff:g id="SERVICE">%1$s</xliff:g> кызматын иштетүү үчүн үндү катуулатуу/акырындатуу баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nБаскычтардын ушул айкалышын башка функцияга дайындоо үчүн, Параметрлер &gt; Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ооба"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Жок"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"КҮЙҮК"</string>
@@ -2069,7 +2069,7 @@
<string name="review_notification_settings_remind_me_action" msgid="1081081018678480907">"Кийинчерээк эскертүү"</string>
<string name="review_notification_settings_dismiss" msgid="4160916504616428294">"Жабуу"</string>
<string name="notification_app_name_system" msgid="3045196791746735601">"Тутум"</string>
- <string name="notification_app_name_settings" msgid="9088548800899952531">"Жөндөөлөр"</string>
+ <string name="notification_app_name_settings" msgid="9088548800899952531">"Параметрлер"</string>
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Камера"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Микрофон"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"экрандагы башка терезелердин үстүнөн көрсөтүлүүдө"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 42a477b4b1a8..9768208e5171 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1534,7 +1534,7 @@
<string name="add_account_button_label" msgid="322390749416414097">"Adicionar conta"</string>
<string name="number_picker_increment_button" msgid="7621013714795186298">"Aumentar"</string>
<string name="number_picker_decrement_button" msgid="5116948444762708204">"Diminuir"</string>
- <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> toque e mantenha pressionado."</string>
+ <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> toque e pressione."</string>
<string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Deslize para cima para aumentar e para baixo para diminuir."</string>
<string name="time_picker_increment_minute_button" msgid="7195870222945784300">"Aumentar minuto"</string>
<string name="time_picker_decrement_minute_button" msgid="230925389943411490">"Diminuir minuto"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 42a477b4b1a8..9768208e5171 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1534,7 +1534,7 @@
<string name="add_account_button_label" msgid="322390749416414097">"Adicionar conta"</string>
<string name="number_picker_increment_button" msgid="7621013714795186298">"Aumentar"</string>
<string name="number_picker_decrement_button" msgid="5116948444762708204">"Diminuir"</string>
- <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> toque e mantenha pressionado."</string>
+ <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> toque e pressione."</string>
<string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Deslize para cima para aumentar e para baixo para diminuir."</string>
<string name="time_picker_increment_minute_button" msgid="7195870222945784300">"Aumentar minuto"</string>
<string name="time_picker_decrement_minute_button" msgid="230925389943411490">"Diminuir minuto"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 93d907ecfd1a..3b3fa06fd93c 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -796,7 +796,7 @@
<string-array name="phoneTypes">
<item msgid="8996339953292723951">"Nhà riêng"</item>
<item msgid="7740243458912727194">"Di động"</item>
- <item msgid="8526146065496663766">"Cơ quan"</item>
+ <item msgid="8526146065496663766">"Nơi làm việc"</item>
<item msgid="8150904584178569699">"Số fax cơ quan"</item>
<item msgid="4537253139152229577">"Số fax nhà riêng"</item>
<item msgid="6751245029698664340">"Số máy nhắn tin"</item>
@@ -805,24 +805,24 @@
</string-array>
<string-array name="emailAddressTypes">
<item msgid="7786349763648997741">"Nhà riêng"</item>
- <item msgid="435564470865989199">"Cơ quan"</item>
+ <item msgid="435564470865989199">"Nơi làm việc"</item>
<item msgid="4199433197875490373">"Khác"</item>
<item msgid="3233938986670468328">"Tùy chỉnh"</item>
</string-array>
<string-array name="postalAddressTypes">
<item msgid="3861463339764243038">"Nhà riêng"</item>
- <item msgid="5472578890164979109">"Cơ quan"</item>
+ <item msgid="5472578890164979109">"Nơi làm việc"</item>
<item msgid="5718921296646594739">"Khác"</item>
<item msgid="5523122236731783179">"Tùy chỉnh"</item>
</string-array>
<string-array name="imAddressTypes">
<item msgid="588088543406993772">"Nhà riêng"</item>
- <item msgid="5503060422020476757">"Cơ quan"</item>
+ <item msgid="5503060422020476757">"Nơi làm việc"</item>
<item msgid="2530391194653760297">"Khác"</item>
<item msgid="7640927178025203330">"Tùy chỉnh"</item>
</string-array>
<string-array name="organizationTypes">
- <item msgid="6144047813304847762">"Cơ quan"</item>
+ <item msgid="6144047813304847762">"Nơi làm việc"</item>
<item msgid="7402720230065674193">"Khác"</item>
<item msgid="808230403067569648">"Tùy chỉnh"</item>
</string-array>
@@ -839,7 +839,7 @@
<string name="phoneTypeCustom" msgid="5120365721260686814">"Tùy chỉnh"</string>
<string name="phoneTypeHome" msgid="3880132427643623588">"Nhà riêng"</string>
<string name="phoneTypeMobile" msgid="1178852541462086735">"Di động"</string>
- <string name="phoneTypeWork" msgid="6604967163358864607">"Cơ quan"</string>
+ <string name="phoneTypeWork" msgid="6604967163358864607">"Nơi làm việc"</string>
<string name="phoneTypeFaxWork" msgid="6757519896109439123">"Số fax cơ quan"</string>
<string name="phoneTypeFaxHome" msgid="6678559953115904345">"Số fax nhà riêng"</string>
<string name="phoneTypePager" msgid="576402072263522767">"Số máy nhắn tin"</string>
@@ -863,16 +863,16 @@
<string name="eventTypeOther" msgid="530671238533887997">"Khác"</string>
<string name="emailTypeCustom" msgid="1809435350482181786">"Tùy chỉnh"</string>
<string name="emailTypeHome" msgid="1597116303154775999">"Nhà riêng"</string>
- <string name="emailTypeWork" msgid="2020095414401882111">"Cơ quan"</string>
+ <string name="emailTypeWork" msgid="2020095414401882111">"Nơi làm việc"</string>
<string name="emailTypeOther" msgid="5131130857030897465">"Khác"</string>
<string name="emailTypeMobile" msgid="787155077375364230">"Di Động"</string>
<string name="postalTypeCustom" msgid="5645590470242939129">"Tùy chỉnh"</string>
<string name="postalTypeHome" msgid="7562272480949727912">"Nhà riêng"</string>
- <string name="postalTypeWork" msgid="8553425424652012826">"Cơ quan"</string>
+ <string name="postalTypeWork" msgid="8553425424652012826">"Nơi làm việc"</string>
<string name="postalTypeOther" msgid="7094245413678857420">"Khác"</string>
<string name="imTypeCustom" msgid="5653384545085765570">"Tùy chỉnh"</string>
<string name="imTypeHome" msgid="6996507981044278216">"Nhà riêng"</string>
- <string name="imTypeWork" msgid="2099668940169903123">"Cơ quan"</string>
+ <string name="imTypeWork" msgid="2099668940169903123">"Nơi làm việc"</string>
<string name="imTypeOther" msgid="8068447383276219810">"Khác"</string>
<string name="imProtocolCustom" msgid="4437878287653764692">"Tùy chỉnh"</string>
<string name="imProtocolAim" msgid="4050198236506604378">"AIM"</string>
@@ -884,7 +884,7 @@
<string name="imProtocolIcq" msgid="2410325380427389521">"ICQ"</string>
<string name="imProtocolJabber" msgid="7919269388889582015">"Jabber"</string>
<string name="imProtocolNetMeeting" msgid="4985002408136148256">"NetMeeting"</string>
- <string name="orgTypeWork" msgid="8684458700669564172">"Cơ quan"</string>
+ <string name="orgTypeWork" msgid="8684458700669564172">"Nơi làm việc"</string>
<string name="orgTypeOther" msgid="5450675258408005553">"Khác"</string>
<string name="orgTypeCustom" msgid="1126322047677329218">"Tùy chỉnh"</string>
<string name="relationTypeCustom" msgid="282938315217441351">"Tùy chỉnh"</string>
@@ -904,7 +904,7 @@
<string name="relationTypeSpouse" msgid="6916682664436031703">"Vợ/chồng"</string>
<string name="sipAddressTypeCustom" msgid="6283889809842649336">"Tùy chỉnh"</string>
<string name="sipAddressTypeHome" msgid="5918441930656878367">"Nhà riêng"</string>
- <string name="sipAddressTypeWork" msgid="7873967986701216770">"Cơ quan"</string>
+ <string name="sipAddressTypeWork" msgid="7873967986701216770">"Nơi làm việc"</string>
<string name="sipAddressTypeOther" msgid="6317012577345187275">"Khác"</string>
<string name="quick_contacts_not_available" msgid="1262709196045052223">"Không tìm thấy ứng dụng nào để xem liên hệ này."</string>
<string name="keyguard_password_enter_pin_code" msgid="6401406801060956153">"Nhập mã PIN"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2f5efd12a2ba..5f0753bb1e0c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -576,6 +576,10 @@
rotations as the default behavior. -->
<bool name="config_allowAllRotations">false</bool>
+ <!-- If false and config_allowAllRotations is false, the screen will rotate to the natural
+ orientation of the device when the auto-rotate policy is toggled. -->
+ <bool name="config_useCurrentRotationOnRotationLockChange">false</bool>
+
<!-- If true, the direction rotation is applied to get to an application's requested
orientation is reversed. Normally, the model is that landscape is
clockwise from portrait; thus on a portrait device an app requesting
@@ -4972,9 +4976,8 @@
<!-- If face auth sends the user directly to home/last open app, or stays on keyguard -->
<bool name="config_faceAuthDismissesKeyguard">true</bool>
- <!-- Default value for whether a SFPS device is required to be interactive for fingerprint auth
- to unlock the device. -->
- <bool name="config_requireScreenOnToAuthEnabled">false</bool>
+ <!-- Default value for performant auth feature. -->
+ <bool name="config_performantAuthDefault">false</bool>
<!-- The component name for the default profile supervisor, which can be set as a profile owner
even after user setup is complete. The defined component should be used for supervision purposes
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 41281fa0d40f..4d4be2256471 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1732,6 +1732,7 @@
<java-symbol type="attr" name="dialogTitleDecorLayout" />
<java-symbol type="attr" name="dialogTitleIconsDecorLayout" />
<java-symbol type="bool" name="config_allowAllRotations" />
+ <java-symbol type="bool" name="config_useCurrentRotationOnRotationLockChange"/>
<java-symbol type="bool" name="config_annoy_dianne" />
<java-symbol type="bool" name="config_startDreamImmediatelyOnDock" />
<java-symbol type="bool" name="config_carDockEnablesAccelerometer" />
@@ -2724,7 +2725,7 @@
<java-symbol type="array" name="config_face_acquire_vendor_biometricprompt_ignorelist" />
<java-symbol type="bool" name="config_faceAuthSupportsSelfIllumination" />
<java-symbol type="bool" name="config_faceAuthDismissesKeyguard" />
- <java-symbol type="bool" name="config_requireScreenOnToAuthEnabled" />
+ <java-symbol type="bool" name="config_performantAuthDefault" />
<!-- Face config -->
<java-symbol type="integer" name="config_faceMaxTemplatesPerUser" />
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index ed2b10117308..3768063f2a91 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -368,4 +368,20 @@ public class PropertyInvalidatedCacheTests {
PropertyInvalidatedCache.MODULE_BLUETOOTH, "getState");
assertEquals(n1, "cache_key.bluetooth.get_state");
}
+
+ @Test
+ public void testOnTrimMemory() {
+ TestCache cache = new TestCache(MODULE, "trimMemoryTest");
+ // The cache is not active until it has been invalidated once.
+ cache.invalidateCache();
+ // Populate the cache with six entries.
+ for (int i = 0; i < 6; i++) {
+ cache.query(i);
+ }
+ // The maximum number of entries in TestCache is 4, so even though six entries were
+ // created, only four are retained.
+ assertEquals(4, cache.size());
+ PropertyInvalidatedCache.onTrimMemory();
+ assertEquals(0, cache.size());
+ }
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index ff1256b47429..a288fd6a3067 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -77,6 +77,7 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
+import androidx.window.extensions.core.util.function.Function;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import androidx.window.extensions.layout.WindowLayoutInfo;
@@ -562,10 +563,10 @@ public class SplitPresenterTest {
SplitAttributes.SplitType.RatioSplitType.splitEqually()
)
).build();
+ final Function<SplitAttributesCalculatorParams, SplitAttributes> calculator =
+ params -> splitAttributes;
- mController.setSplitAttributesCalculator(params -> {
- return splitAttributes;
- });
+ mController.setSplitAttributesCalculator(calculator);
assertEquals(splitAttributes, mPresenter.computeSplitAttributes(taskProperties,
splitPairRule, null /* minDimensionsPair */));
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 5de5365e4d03..cddbf469b3c7 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/res/drawable/caption_decor_title.xml b/libs/WindowManager/Shell/res/drawable/caption_decor_title.xml
new file mode 100644
index 000000000000..6114ad6e277a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/caption_decor_title.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape android:shape="rectangle"
+ android:tintMode="multiply"
+ android:tint="@color/decor_title_color"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@android:color/white" />
+</shape>
diff --git a/libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml
index b7ff96e64eec..91edbf1a7bd4 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml
+++ b/libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml
@@ -14,10 +14,10 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24"
+ android:width="32.0dp"
+ android:height="32.0dp"
+ android:viewportWidth="32.0"
+ android:viewportHeight="32.0"
android:tint="@color/decor_button_dark_color">
<path
android:fillColor="@android:color/white" android:pathData="M6,21V19H18V21Z"/>
diff --git a/libs/WindowManager/Shell/res/layout/caption_window_decor.xml b/libs/WindowManager/Shell/res/layout/caption_window_decor.xml
new file mode 100644
index 000000000000..f3d219872001
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/caption_window_decor.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/caption"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="end"
+ android:background="@drawable/caption_decor_title">
+ <Button
+ style="@style/CaptionButtonStyle"
+ android:id="@+id/back_button"
+ android:layout_gravity="center_vertical|end"
+ android:contentDescription="@string/back_button_text"
+ android:background="@drawable/decor_back_button_dark"
+ android:duplicateParentState="true"/>
+ <Space
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:elevation="2dp"/>
+ <Button
+ style="@style/CaptionButtonStyle"
+ android:id="@+id/minimize_window"
+ android:layout_gravity="center_vertical|end"
+ android:contentDescription="@string/minimize_button_text"
+ android:background="@drawable/decor_minimize_button_dark"
+ android:duplicateParentState="true"/>
+ <Button
+ style="@style/CaptionButtonStyle"
+ android:id="@+id/maximize_window"
+ android:layout_gravity="center_vertical|end"
+ android:contentDescription="@string/maximize_button_text"
+ android:background="@drawable/decor_maximize_button_dark"
+ android:duplicateParentState="true"/>
+ <Button
+ style="@style/CaptionButtonStyle"
+ android:id="@+id/close_window"
+ android:contentDescription="@string/close_button_text"
+ android:background="@drawable/decor_close_button_dark"
+ android:duplicateParentState="true"/>
+</com.android.wm.shell.windowdecor.WindowDecorLinearLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 7d39c45d2530..800728b1e1e4 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -19,7 +19,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="pip_phone_close" msgid="5783752637260411309">"Жабуу"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Жайып көрсөтүү"</string>
- <string name="pip_phone_settings" msgid="5468987116750491918">"Жөндөөлөр"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Параметрлер"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Экранды бөлүү режимине өтүү"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Меню"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> – сүрөт ичиндеги сүрөт"</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index a9d3c9f154cd..abb357c5b653 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -78,6 +78,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
private final Rect mResizingBounds = new Rect();
private final Rect mTempRect = new Rect();
private ValueAnimator mFadeAnimator;
+ private ValueAnimator mScreenshotAnimator;
private int mIconSize;
private int mOffsetX;
@@ -135,8 +136,17 @@ public class SplitDecorManager extends WindowlessWindowManager {
/** Releases the surfaces for split decor. */
public void release(SurfaceControl.Transaction t) {
- if (mFadeAnimator != null && mFadeAnimator.isRunning()) {
- mFadeAnimator.cancel();
+ if (mFadeAnimator != null) {
+ if (mFadeAnimator.isRunning()) {
+ mFadeAnimator.cancel();
+ }
+ mFadeAnimator = null;
+ }
+ if (mScreenshotAnimator != null) {
+ if (mScreenshotAnimator.isRunning()) {
+ mScreenshotAnimator.cancel();
+ }
+ mScreenshotAnimator = null;
}
if (mViewHost != null) {
mViewHost.release();
@@ -238,16 +248,20 @@ public class SplitDecorManager extends WindowlessWindowManager {
/** Stops showing resizing hint. */
public void onResized(SurfaceControl.Transaction t, Runnable animFinishedCallback) {
if (mScreenshot != null) {
+ if (mScreenshotAnimator != null && mScreenshotAnimator.isRunning()) {
+ mScreenshotAnimator.cancel();
+ }
+
t.setPosition(mScreenshot, mOffsetX, mOffsetY);
final SurfaceControl.Transaction animT = new SurfaceControl.Transaction();
- final ValueAnimator va = ValueAnimator.ofFloat(1, 0);
- va.addUpdateListener(valueAnimator -> {
+ mScreenshotAnimator = ValueAnimator.ofFloat(1, 0);
+ mScreenshotAnimator.addUpdateListener(valueAnimator -> {
final float progress = (float) valueAnimator.getAnimatedValue();
animT.setAlpha(mScreenshot, progress);
animT.apply();
});
- va.addListener(new AnimatorListenerAdapter() {
+ mScreenshotAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mRunningAnimationCount++;
@@ -266,7 +280,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
}
}
});
- va.start();
+ mScreenshotAnimator.start();
}
if (mResizingIconView == null) {
@@ -292,9 +306,6 @@ public class SplitDecorManager extends WindowlessWindowManager {
});
return;
}
-
- // If fade-in animation is running, cancel it and re-run fade-out one.
- mFadeAnimator.cancel();
}
if (mShown) {
fadeOutDecor(animFinishedCallback);
@@ -332,6 +343,11 @@ public class SplitDecorManager extends WindowlessWindowManager {
* directly. */
public void fadeOutDecor(Runnable finishedCallback) {
if (mShown) {
+ // If previous animation is running, just cancel it.
+ if (mFadeAnimator != null && mFadeAnimator.isRunning()) {
+ mFadeAnimator.cancel();
+ }
+
startFadeAnimation(false /* show */, true, finishedCallback);
mShown = false;
} else {
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 d3b9fa5e628d..512a4ef386bb 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
@@ -49,6 +49,7 @@ import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.desktopmode.DesktopModeController;
+import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.draganddrop.DragAndDropController;
@@ -93,6 +94,7 @@ import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
import com.android.wm.shell.unfold.qualifier.UnfoldShellTransition;
import com.android.wm.shell.unfold.qualifier.UnfoldTransition;
+import com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -192,7 +194,8 @@ public abstract class WMShellModule {
SyncTransactionQueue syncQueue,
Optional<DesktopModeController> desktopModeController,
Optional<DesktopTasksController> desktopTasksController) {
- return new DesktopModeWindowDecorViewModel(
+ if (DesktopModeStatus.isAnyEnabled()) {
+ return new DesktopModeWindowDecorViewModel(
context,
mainHandler,
mainChoreographer,
@@ -201,6 +204,14 @@ public abstract class WMShellModule {
syncQueue,
desktopModeController,
desktopTasksController);
+ }
+ return new CaptionWindowDecorViewModel(
+ context,
+ mainHandler,
+ mainChoreographer,
+ taskOrganizer,
+ displayController,
+ syncQueue);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
index 63b03ab76aac..a839a230ea6f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
@@ -36,6 +36,7 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArraySet;
@@ -261,6 +262,11 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
}
}
+ /** Get number of tasks that are marked as visible */
+ int getVisibleTaskCount() {
+ return mDesktopModeTaskRepository.getVisibleTaskCount();
+ }
+
@NonNull
private WindowContainerTransaction bringDesktopAppsToFront(boolean force) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -438,5 +444,15 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
executeRemoteCallWithTaskPermission(mController, "showDesktopApps",
DesktopModeController::showDesktopApps);
}
+
+ @Override
+ public int getVisibleTaskCount() throws RemoteException {
+ int[] result = new int[1];
+ executeRemoteCallWithTaskPermission(mController, "getVisibleTaskCount",
+ controller -> result[0] = controller.getVisibleTaskCount(),
+ true /* blocking */
+ );
+ return result[0];
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 600ccc17ecaa..47342c9f21ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -143,6 +143,13 @@ class DesktopModeTaskRepository {
}
/**
+ * Get number of tasks that are marked as visible
+ */
+ fun getVisibleTaskCount(): Int {
+ return visibleTasks.size
+ }
+
+ /**
* Add (or move if it already exists) the task to the top of the ordered list.
*/
fun addOrMoveFreeformTaskToTop(taskId: Int) {
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 9165f7012bb5..23033253eaf9 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
@@ -96,6 +96,11 @@ class DesktopTasksController(
}
}
+ /** Get number of tasks that are marked as visible */
+ fun getVisibleTaskCount(): Int {
+ return desktopModeTaskRepository.getVisibleTaskCount()
+ }
+
/** Move a task with given `taskId` to desktop */
fun moveToDesktop(taskId: Int) {
shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveToDesktop(task) }
@@ -309,5 +314,16 @@ class DesktopTasksController(
Consumer(DesktopTasksController::showDesktopApps)
)
}
+
+ override fun getVisibleTaskCount(): Int {
+ val result = IntArray(1)
+ ExecutorUtils.executeRemoteCallWithTaskPermission(
+ controller,
+ "getVisibleTaskCount",
+ { controller -> result[0] = controller.getVisibleTaskCount() },
+ true /* blocking */
+ )
+ return result[0]
+ }
}
}
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 5042bd6f2d65..d0739e14675f 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,4 +23,7 @@ interface IDesktopMode {
/** Show apps on the desktop */
void showDesktopApps();
+
+ /** Get count of visible desktop tasks */
+ int getVisibleTaskCount();
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index cd61dbb5b7d1..f6d67d858f98 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -47,7 +47,7 @@ public class PipBoundsAlgorithm {
private final @NonNull PipBoundsState mPipBoundsState;
private final PipSnapAlgorithm mSnapAlgorithm;
- private final PipKeepClearAlgorithm mPipKeepClearAlgorithm;
+ private final PipKeepClearAlgorithmInterface mPipKeepClearAlgorithm;
private float mDefaultSizePercent;
private float mMinAspectRatioForMinSize;
@@ -62,7 +62,7 @@ public class PipBoundsAlgorithm {
public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState,
@NonNull PipSnapAlgorithm pipSnapAlgorithm,
- @NonNull PipKeepClearAlgorithm pipKeepClearAlgorithm) {
+ @NonNull PipKeepClearAlgorithmInterface pipKeepClearAlgorithm) {
mPipBoundsState = pipBoundsState;
mSnapAlgorithm = pipSnapAlgorithm;
mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithmInterface.java
index e3495e100c62..5045cf905ee6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithmInterface.java
@@ -24,7 +24,7 @@ import java.util.Set;
* Interface for interacting with keep clear algorithm used to move PiP window out of the way of
* keep clear areas.
*/
-public interface PipKeepClearAlgorithm {
+public interface PipKeepClearAlgorithmInterface {
/**
* Adjust the position of picture in picture window based on the registered keep clear areas.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
index 690505e03fce..ed8dc7ded654 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
@@ -26,14 +26,14 @@ import android.view.Gravity;
import com.android.wm.shell.R;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipKeepClearAlgorithm;
+import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import java.util.Set;
/**
* Calculates the adjusted position that does not occlude keep clear areas.
*/
-public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithm {
+public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithmInterface {
private boolean mKeepClearAreaGravityEnabled =
SystemProperties.getBoolean(
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 3153313de42f..e83854e22fa2 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
@@ -46,8 +46,6 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.UserManager;
import android.util.Pair;
import android.util.Size;
import android.view.DisplayInfo;
@@ -85,7 +83,7 @@ import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipKeepClearAlgorithm;
+import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -137,7 +135,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private PipAppOpsListener mAppOpsListener;
private PipMediaController mMediaController;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
- private PipKeepClearAlgorithm mPipKeepClearAlgorithm;
+ private PipKeepClearAlgorithmInterface mPipKeepClearAlgorithm;
private PipBoundsState mPipBoundsState;
private PipMotionHelper mPipMotionHelper;
private PipTouchHandler mTouchHandler;
@@ -380,7 +378,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
PipAnimationController pipAnimationController,
PipAppOpsListener pipAppOpsListener,
PipBoundsAlgorithm pipBoundsAlgorithm,
- PipKeepClearAlgorithm pipKeepClearAlgorithm,
+ PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
PipBoundsState pipBoundsState,
PipMotionHelper pipMotionHelper,
PipMediaController pipMediaController,
@@ -419,7 +417,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
PipAnimationController pipAnimationController,
PipAppOpsListener pipAppOpsListener,
PipBoundsAlgorithm pipBoundsAlgorithm,
- PipKeepClearAlgorithm pipKeepClearAlgorithm,
+ PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
@NonNull PipBoundsState pipBoundsState,
PipMotionHelper pipMotionHelper,
PipMediaController pipMediaController,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 83bc7c0e6e7d..850c561c891f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -337,8 +337,10 @@ public class PipTouchHandler {
mMotionHelper.synchronizePinnedStackBounds();
reloadResources();
- // Recreate the dismiss target for the new orientation.
- mPipDismissTargetHandler.createOrUpdateDismissTarget();
+ if (mPipTaskOrganizer.isInPip()) {
+ // Recreate the dismiss target for the new orientation.
+ mPipDismissTargetHandler.createOrUpdateDismissTarget();
+ }
}
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
index ce34d2f9547d..1ff77f7d36dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
@@ -39,7 +39,7 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipKeepClearAlgorithm;
+import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -65,7 +65,7 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
@NonNull TvPipBoundsState tvPipBoundsState,
@NonNull PipSnapAlgorithm pipSnapAlgorithm) {
super(context, tvPipBoundsState, pipSnapAlgorithm,
- new PipKeepClearAlgorithm() {});
+ new PipKeepClearAlgorithmInterface() {});
this.mTvPipBoundsState = tvPipBoundsState;
this.mKeepClearAlgorithm = new TvPipKeepClearAlgorithm();
reloadResources(context);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 1488469759cd..a6c4ac28c1fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1072,6 +1072,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSideStage.removeAllTasks(wct, false /* toTop */);
mMainStage.deactivate(wct, false /* toTop */);
wct.reorder(mRootTaskInfo.token, false /* onTop */);
+ wct.setForceTranslucent(mRootTaskInfo.token, true);
wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
onTransitionAnimationComplete();
} else {
@@ -1103,6 +1104,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mMainStage.deactivate(finishedWCT, childrenToTop == mMainStage /* toTop */);
mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */);
finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
+ finishedWCT.setForceTranslucent(mRootTaskInfo.token, true);
finishedWCT.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
mSyncQueue.queue(finishedWCT);
mSyncQueue.runInSync(at -> {
@@ -1485,6 +1487,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
private void onStageVisibilityChanged(StageListenerImpl stageListener) {
+ // If split didn't active, just ignore this callback because we should already did these
+ // on #applyExitSplitScreen.
+ if (!isSplitActive()) {
+ return;
+ }
+
final boolean sideStageVisible = mSideStageListener.mVisible;
final boolean mainStageVisible = mMainStageListener.mVisible;
@@ -1493,20 +1501,23 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return;
}
+ // Check if it needs to dismiss split screen when both stage invisible.
+ if (!mainStageVisible && mExitSplitScreenOnHide) {
+ exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RETURN_HOME);
+ return;
+ }
+
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (!mainStageVisible) {
+ // Split entering background.
wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
true /* setReparentLeafTaskIfRelaunch */);
wct.setForceTranslucent(mRootTaskInfo.token, true);
- // Both stages are not visible, check if it needs to dismiss split screen.
- if (mExitSplitScreenOnHide) {
- exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RETURN_HOME);
- }
} else {
wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
false /* setReparentLeafTaskIfRelaunch */);
- wct.setForceTranslucent(mRootTaskInfo.token, false);
}
+
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
setDividerVisibility(mainStageVisible, t);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
new file mode 100644
index 000000000000..129924ad5d05
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.os.Handler;
+import android.util.SparseArray;
+import android.view.Choreographer;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+
+/**
+ * View model for the window decoration with a caption and shadows. Works with
+ * {@link CaptionWindowDecoration}.
+ */
+public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final Context mContext;
+ private final Handler mMainHandler;
+ private final Choreographer mMainChoreographer;
+ private final DisplayController mDisplayController;
+ private final SyncTransactionQueue mSyncQueue;
+ private TaskOperations mTaskOperations;
+
+ private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
+
+ public CaptionWindowDecorViewModel(
+ Context context,
+ Handler mainHandler,
+ Choreographer mainChoreographer,
+ ShellTaskOrganizer taskOrganizer,
+ DisplayController displayController,
+ SyncTransactionQueue syncQueue) {
+ mContext = context;
+ mMainHandler = mainHandler;
+ mMainChoreographer = mainChoreographer;
+ mTaskOrganizer = taskOrganizer;
+ mDisplayController = displayController;
+ mSyncQueue = syncQueue;
+ }
+
+ @Override
+ public void setFreeformTaskTransitionStarter(FreeformTaskTransitionStarter transitionStarter) {
+ mTaskOperations = new TaskOperations(transitionStarter, mContext, mSyncQueue);
+ }
+
+ @Override
+ public boolean onTaskOpening(
+ RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ if (!shouldShowWindowDecor(taskInfo)) return false;
+ createWindowDecoration(taskInfo, taskSurface, startT, finishT);
+ return true;
+ }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+ final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+
+ if (decoration == null) return;
+
+ decoration.relayout(taskInfo);
+ setupCaptionColor(taskInfo, decoration);
+ }
+
+ @Override
+ public void onTaskChanging(
+ RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+
+ if (!shouldShowWindowDecor(taskInfo)) {
+ if (decoration != null) {
+ destroyWindowDecoration(taskInfo);
+ }
+ return;
+ }
+
+ if (decoration == null) {
+ createWindowDecoration(taskInfo, taskSurface, startT, finishT);
+ } else {
+ decoration.relayout(taskInfo, startT, finishT);
+ }
+ }
+
+ @Override
+ public void onTaskClosing(
+ RunningTaskInfo taskInfo,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+ if (decoration == null) return;
+
+ decoration.relayout(taskInfo, startT, finishT);
+ }
+
+ @Override
+ public void destroyWindowDecoration(RunningTaskInfo taskInfo) {
+ final CaptionWindowDecoration decoration =
+ mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId);
+ if (decoration == null) return;
+
+ decoration.close();
+ }
+
+ private void setupCaptionColor(RunningTaskInfo taskInfo, CaptionWindowDecoration decoration) {
+ final int statusBarColor = taskInfo.taskDescription.getStatusBarColor();
+ decoration.setCaptionColor(statusBarColor);
+ }
+
+ private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
+ return taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
+ || (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
+ && taskInfo.configuration.windowConfiguration.getDisplayWindowingMode()
+ == WINDOWING_MODE_FREEFORM);
+ }
+
+ private void createWindowDecoration(
+ RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ final CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+ if (oldDecoration != null) {
+ // close the old decoration if it exists to avoid two window decorations being added
+ oldDecoration.close();
+ }
+ final CaptionWindowDecoration windowDecoration =
+ new CaptionWindowDecoration(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ taskSurface,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+ mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
+
+ final TaskPositioner taskPositioner =
+ new TaskPositioner(mTaskOrganizer, windowDecoration);
+ final CaptionTouchEventListener touchEventListener =
+ new CaptionTouchEventListener(taskInfo, taskPositioner);
+ windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
+ windowDecoration.setDragResizeCallback(taskPositioner);
+ windowDecoration.relayout(taskInfo, startT, finishT);
+ setupCaptionColor(taskInfo, windowDecoration);
+ }
+
+ private class CaptionTouchEventListener implements
+ View.OnClickListener, View.OnTouchListener {
+
+ private final int mTaskId;
+ private final WindowContainerToken mTaskToken;
+ private final DragResizeCallback mDragResizeCallback;
+
+ private int mDragPointerId = -1;
+
+ private CaptionTouchEventListener(
+ RunningTaskInfo taskInfo,
+ DragResizeCallback dragResizeCallback) {
+ mTaskId = taskInfo.taskId;
+ mTaskToken = taskInfo.token;
+ mDragResizeCallback = dragResizeCallback;
+ }
+
+ @Override
+ public void onClick(View v) {
+ final int id = v.getId();
+ if (id == R.id.close_window) {
+ mTaskOperations.closeTask(mTaskToken);
+ } else if (id == R.id.back_button) {
+ mTaskOperations.injectBackKey();
+ } else if (id == R.id.minimize_window) {
+ mTaskOperations.minimizeTask(mTaskToken);
+ } else if (id == R.id.maximize_window) {
+ RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+ mTaskOperations.maximizeTask(taskInfo);
+ }
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent e) {
+ if (v.getId() != R.id.caption) {
+ return false;
+ }
+ handleEventForMove(e);
+
+ if (e.getAction() != MotionEvent.ACTION_DOWN) {
+ return false;
+ }
+ final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+ if (taskInfo.isFocused) {
+ return false;
+ }
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reorder(mTaskToken, true /* onTop */);
+ mSyncQueue.queue(wct);
+ return true;
+ }
+
+ /**
+ * @param e {@link MotionEvent} to process
+ * @return {@code true} if a drag is happening; or {@code false} if it is not
+ */
+ private void handleEventForMove(MotionEvent e) {
+ final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+ if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ return;
+ }
+ switch (e.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ mDragPointerId = e.getPointerId(0);
+ mDragResizeCallback.onDragResizeStart(
+ 0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ int dragPointerIdx = e.findPointerIndex(mDragPointerId);
+ mDragResizeCallback.onDragResizeMove(
+ e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
+ break;
+ }
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL: {
+ int dragPointerIdx = e.findPointerIndex(mDragPointerId);
+ mDragResizeCallback.onDragResizeEnd(
+ e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
new file mode 100644
index 000000000000..d26f1fc8ef1b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.VectorDrawable;
+import android.os.Handler;
+import android.view.Choreographer;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+/**
+ * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
+ * {@link CaptionWindowDecorViewModel}. The caption bar contains a back button, minimize button,
+ * maximize button and close button.
+ */
+public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> {
+ private final Handler mHandler;
+ private final Choreographer mChoreographer;
+ private final SyncTransactionQueue mSyncQueue;
+
+ private View.OnClickListener mOnCaptionButtonClickListener;
+ private View.OnTouchListener mOnCaptionTouchListener;
+ private DragResizeCallback mDragResizeCallback;
+ private DragResizeInputListener mDragResizeListener;
+ private final DragDetector mDragDetector;
+
+ private RelayoutParams mRelayoutParams = new RelayoutParams();
+ private final RelayoutResult<WindowDecorLinearLayout> mResult =
+ new RelayoutResult<>();
+
+ CaptionWindowDecoration(
+ Context context,
+ DisplayController displayController,
+ ShellTaskOrganizer taskOrganizer,
+ RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ Handler handler,
+ Choreographer choreographer,
+ SyncTransactionQueue syncQueue) {
+ super(context, displayController, taskOrganizer, taskInfo, taskSurface);
+
+ mHandler = handler;
+ mChoreographer = choreographer;
+ mSyncQueue = syncQueue;
+ mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
+ }
+
+ void setCaptionListeners(
+ View.OnClickListener onCaptionButtonClickListener,
+ View.OnTouchListener onCaptionTouchListener) {
+ mOnCaptionButtonClickListener = onCaptionButtonClickListener;
+ mOnCaptionTouchListener = onCaptionTouchListener;
+ }
+
+ void setDragResizeCallback(DragResizeCallback dragResizeCallback) {
+ mDragResizeCallback = dragResizeCallback;
+ }
+
+ @Override
+ void relayout(RunningTaskInfo taskInfo) {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ relayout(taskInfo, t, t);
+ mSyncQueue.runInSync(transaction -> {
+ transaction.merge(t);
+ t.close();
+ });
+ }
+
+ void relayout(RunningTaskInfo taskInfo,
+ SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
+ final int shadowRadiusID = taskInfo.isFocused
+ ? R.dimen.freeform_decor_shadow_focused_thickness
+ : R.dimen.freeform_decor_shadow_unfocused_thickness;
+ final boolean isFreeform =
+ taskInfo.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FREEFORM;
+ final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
+
+ final WindowDecorLinearLayout oldRootView = mResult.mRootView;
+ final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ final int outsetLeftId = R.dimen.freeform_resize_handle;
+ final int outsetTopId = R.dimen.freeform_resize_handle;
+ final int outsetRightId = R.dimen.freeform_resize_handle;
+ final int outsetBottomId = R.dimen.freeform_resize_handle;
+
+ mRelayoutParams.reset();
+ mRelayoutParams.mRunningTaskInfo = taskInfo;
+ mRelayoutParams.mLayoutResId = R.layout.caption_window_decor;
+ mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
+ mRelayoutParams.mShadowRadiusId = shadowRadiusID;
+ if (isDragResizeable) {
+ mRelayoutParams.setOutsets(outsetLeftId, outsetTopId, outsetRightId, outsetBottomId);
+ }
+
+ relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
+ // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
+
+ mTaskOrganizer.applyTransaction(wct);
+
+ if (mResult.mRootView == null) {
+ // This means something blocks the window decor from showing, e.g. the task is hidden.
+ // Nothing is set up in this case including the decoration surface.
+ return;
+ }
+ if (oldRootView != mResult.mRootView) {
+ setupRootView();
+ }
+
+ if (!isDragResizeable) {
+ closeDragResizeListener();
+ return;
+ }
+
+ if (oldDecorationSurface != mDecorationContainerSurface || mDragResizeListener == null) {
+ closeDragResizeListener();
+ mDragResizeListener = new DragResizeInputListener(
+ mContext,
+ mHandler,
+ mChoreographer,
+ mDisplay.getDisplayId(),
+ mDecorationContainerSurface,
+ mDragResizeCallback);
+ }
+
+ final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
+ .getScaledTouchSlop();
+ mDragDetector.setTouchSlop(touchSlop);
+
+ final int resize_handle = mResult.mRootView.getResources()
+ .getDimensionPixelSize(R.dimen.freeform_resize_handle);
+ final int resize_corner = mResult.mRootView.getResources()
+ .getDimensionPixelSize(R.dimen.freeform_resize_corner);
+ mDragResizeListener.setGeometry(
+ mResult.mWidth, mResult.mHeight, resize_handle, resize_corner, touchSlop);
+ }
+
+ /**
+ * Sets up listeners when a new root view is created.
+ */
+ private void setupRootView() {
+ final View caption = mResult.mRootView.findViewById(R.id.caption);
+ caption.setOnTouchListener(mOnCaptionTouchListener);
+ final View close = caption.findViewById(R.id.close_window);
+ close.setOnClickListener(mOnCaptionButtonClickListener);
+ final View back = caption.findViewById(R.id.back_button);
+ back.setOnClickListener(mOnCaptionButtonClickListener);
+ final View minimize = caption.findViewById(R.id.minimize_window);
+ minimize.setOnClickListener(mOnCaptionButtonClickListener);
+ final View maximize = caption.findViewById(R.id.maximize_window);
+ maximize.setOnClickListener(mOnCaptionButtonClickListener);
+ }
+
+ void setCaptionColor(int captionColor) {
+ if (mResult.mRootView == null) {
+ return;
+ }
+
+ final View caption = mResult.mRootView.findViewById(R.id.caption);
+ final GradientDrawable captionDrawable = (GradientDrawable) caption.getBackground();
+ captionDrawable.setColor(captionColor);
+
+ final int buttonTintColorRes =
+ Color.valueOf(captionColor).luminance() < 0.5
+ ? R.color.decor_button_light_color
+ : R.color.decor_button_dark_color;
+ final ColorStateList buttonTintColor =
+ caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */);
+
+ final View back = caption.findViewById(R.id.back_button);
+ final VectorDrawable backBackground = (VectorDrawable) back.getBackground();
+ backBackground.setTintList(buttonTintColor);
+
+ final View minimize = caption.findViewById(R.id.minimize_window);
+ final VectorDrawable minimizeBackground = (VectorDrawable) minimize.getBackground();
+ minimizeBackground.setTintList(buttonTintColor);
+
+ final View maximize = caption.findViewById(R.id.maximize_window);
+ final VectorDrawable maximizeBackground = (VectorDrawable) maximize.getBackground();
+ maximizeBackground.setTintList(buttonTintColor);
+
+ final View close = caption.findViewById(R.id.close_window);
+ final VectorDrawable closeBackground = (VectorDrawable) close.getBackground();
+ closeBackground.setTintList(buttonTintColor);
+ }
+
+ private void closeDragResizeListener() {
+ if (mDragResizeListener == null) {
+ return;
+ }
+ mDragResizeListener.close();
+ mDragResizeListener = null;
+ }
+
+ @Override
+ public void close() {
+ closeDragResizeListener();
+ super.close();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 00aab6733369..2863adcc8f5e 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
@@ -27,17 +27,12 @@ import android.content.Context;
import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
-import android.os.SystemClock;
-import android.util.Log;
import android.util.SparseArray;
import android.view.Choreographer;
import android.view.InputChannel;
-import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.InputMonitor;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
@@ -55,7 +50,6 @@ import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
-import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -74,9 +68,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private final Choreographer mMainChoreographer;
private final DisplayController mDisplayController;
private final SyncTransactionQueue mSyncQueue;
- private FreeformTaskTransitionStarter mTransitionStarter;
- private Optional<DesktopModeController> mDesktopModeController;
- private Optional<DesktopTasksController> mDesktopTasksController;
+ private final Optional<DesktopModeController> mDesktopModeController;
+ private final Optional<DesktopTasksController> mDesktopTasksController;
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -84,7 +77,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private final SparseArray<DesktopModeWindowDecoration> mWindowDecorByTaskId =
new SparseArray<>();
private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl();
- private InputMonitorFactory mInputMonitorFactory;
+ private final InputMonitorFactory mInputMonitorFactory;
+ private TaskOperations mTaskOperations;
public DesktopModeWindowDecorViewModel(
Context context,
@@ -136,7 +130,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
@Override
public void setFreeformTaskTransitionStarter(FreeformTaskTransitionStarter transitionStarter) {
- mTransitionStarter = transitionStarter;
+ mTaskOperations = new TaskOperations(transitionStarter, mContext, mSyncQueue);
}
@Override
@@ -204,13 +198,13 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
if (decoration == null) return;
decoration.close();
- int displayId = taskInfo.displayId;
+ final int displayId = taskInfo.displayId;
if (mEventReceiversByDisplay.contains(displayId)) {
removeTaskFromEventReceiver(displayId);
}
}
- private class CaptionTouchEventListener implements
+ private class DesktopModeTouchEventListener implements
View.OnClickListener, View.OnTouchListener {
private final int mTaskId;
@@ -220,7 +214,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private int mDragPointerId = -1;
- private CaptionTouchEventListener(
+ private DesktopModeTouchEventListener(
RunningTaskInfo taskInfo,
DragResizeCallback dragResizeCallback,
DragDetector dragDetector) {
@@ -232,18 +226,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
@Override
public void onClick(View v) {
- DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
final int id = v.getId();
if (id == R.id.close_window) {
- WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.removeTask(mTaskToken);
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mTransitionStarter.startRemoveTransition(wct);
- } else {
- mSyncQueue.queue(wct);
- }
+ mTaskOperations.closeTask(mTaskToken);
} else if (id == R.id.back_button) {
- injectBackKey();
+ mTaskOperations.injectBackKey();
} else if (id == R.id.caption_handle) {
decoration.createHandleMenu();
} else if (id == R.id.desktop_button) {
@@ -258,29 +246,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
}
- private void injectBackKey() {
- sendBackEvent(KeyEvent.ACTION_DOWN);
- sendBackEvent(KeyEvent.ACTION_UP);
- }
-
- private void sendBackEvent(int action) {
- final long when = SystemClock.uptimeMillis();
- final KeyEvent ev = new KeyEvent(when, when, action, KeyEvent.KEYCODE_BACK,
- 0 /* repeat */, 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD,
- 0 /* scancode */, KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
- InputDevice.SOURCE_KEYBOARD);
-
- ev.setDisplayId(mContext.getDisplay().getDisplayId());
- if (!InputManager.getInstance()
- .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC)) {
- Log.e(TAG, "Inject input event fail");
- }
- }
-
@Override
public boolean onTouch(View v, MotionEvent e) {
boolean isDrag = false;
- int id = v.getId();
+ final int id = v.getId();
if (id != R.id.caption_handle && id != R.id.desktop_mode_caption) {
return false;
}
@@ -291,11 +260,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
if (e.getAction() != MotionEvent.ACTION_DOWN) {
return isDrag;
}
- RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+ final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
if (taskInfo.isFocused) {
return isDrag;
}
- WindowContainerTransaction wct = new WindowContainerTransaction();
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reorder(mTaskToken, true /* onTop */);
mSyncQueue.queue(wct);
return true;
@@ -306,7 +275,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
* @return {@code true} if a drag is happening; or {@code false} if it is not
*/
private void handleEventForMove(MotionEvent e) {
- RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+ final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
if (DesktopModeStatus.isProto2Enabled()
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
return;
@@ -325,16 +294,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
break;
}
case MotionEvent.ACTION_MOVE: {
- int dragPointerIdx = e.findPointerIndex(mDragPointerId);
+ final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
mDragResizeCallback.onDragResizeMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
- int dragPointerIdx = e.findPointerIndex(mDragPointerId);
- int statusBarHeight = mDisplayController.getDisplayLayout(taskInfo.displayId)
- .stableInsets().top;
+ final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
+ final int statusBarHeight = mDisplayController
+ .getDisplayLayout(taskInfo.displayId).stableInsets().top;
mDragResizeCallback.onDragResizeEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
if (e.getRawY(dragPointerIdx) <= statusBarHeight) {
@@ -408,7 +377,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
*/
private void incrementEventReceiverTasks(int displayId) {
if (mEventReceiversByDisplay.contains(displayId)) {
- EventReceiver eventReceiver = mEventReceiversByDisplay.get(displayId);
+ final EventReceiver eventReceiver = mEventReceiversByDisplay.get(displayId);
eventReceiver.incrementTaskNumber();
} else {
createInputChannel(displayId);
@@ -418,7 +387,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
// If all tasks on this display are gone, we don't need to monitor its input.
private void removeTaskFromEventReceiver(int displayId) {
if (!mEventReceiversByDisplay.contains(displayId)) return;
- EventReceiver eventReceiver = mEventReceiversByDisplay.get(displayId);
+ final EventReceiver eventReceiver = mEventReceiversByDisplay.get(displayId);
if (eventReceiver == null) return;
eventReceiver.decrementTaskNumber();
if (eventReceiver.getTasksOnDisplay() == 0) {
@@ -433,7 +402,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
*/
private void handleReceivedMotionEvent(MotionEvent ev, InputMonitor inputMonitor) {
if (DesktopModeStatus.isProto2Enabled()) {
- DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
+ final DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
if (focusedDecor == null
|| focusedDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
handleCaptionThroughStatusBar(ev);
@@ -458,9 +427,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
// If an UP/CANCEL action is received outside of caption bounds, turn off handle menu
private void handleEventOutsideFocusedCaption(MotionEvent ev) {
- int action = ev.getActionMasked();
+ final int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
- DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
+ final DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
if (focusedDecor == null) {
return;
}
@@ -480,7 +449,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
// Begin drag through status bar if applicable.
- DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
+ final DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
if (focusedDecor != null) {
boolean dragFromStatusBarAllowed = false;
if (DesktopModeStatus.isProto2Enabled()) {
@@ -499,14 +468,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
break;
}
case MotionEvent.ACTION_UP: {
- DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
+ final DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
if (focusedDecor == null) {
mTransitionDragActive = false;
return;
}
if (mTransitionDragActive) {
mTransitionDragActive = false;
- int statusBarHeight = mDisplayController
+ final int statusBarHeight = mDisplayController
.getDisplayLayout(focusedDecor.mTaskInfo.displayId).stableInsets().top;
if (ev.getY() > statusBarHeight) {
if (DesktopModeStatus.isProto2Enabled()) {
@@ -530,10 +499,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
@Nullable
private DesktopModeWindowDecoration getFocusedDecor() {
- int size = mWindowDecorByTaskId.size();
+ final int size = mWindowDecorByTaskId.size();
DesktopModeWindowDecoration focusedDecor = null;
for (int i = 0; i < size; i++) {
- DesktopModeWindowDecoration decor = mWindowDecorByTaskId.valueAt(i);
+ final DesktopModeWindowDecoration decor = mWindowDecorByTaskId.valueAt(i);
if (decor != null && decor.isFocused()) {
focusedDecor = decor;
break;
@@ -543,16 +512,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
private void createInputChannel(int displayId) {
- InputManager inputManager = InputManager.getInstance();
- InputMonitor inputMonitor =
+ final InputManager inputManager = InputManager.getInstance();
+ final InputMonitor inputMonitor =
mInputMonitorFactory.create(inputManager, mContext);
- EventReceiver eventReceiver = new EventReceiver(inputMonitor,
+ final EventReceiver eventReceiver = new EventReceiver(inputMonitor,
inputMonitor.getInputChannel(), Looper.myLooper());
mEventReceiversByDisplay.put(displayId, eventReceiver);
}
private void disposeInputChannel(int displayId) {
- EventReceiver eventReceiver = mEventReceiversByDisplay.removeReturnOld(displayId);
+ final EventReceiver eventReceiver = mEventReceiversByDisplay.removeReturnOld(displayId);
if (eventReceiver != null) {
eventReceiver.dispose();
}
@@ -571,7 +540,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- DesktopModeWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+ final DesktopModeWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (oldDecoration != null) {
// close the old decoration if it exists to avoid two window decorations being added
oldDecoration.close();
@@ -588,10 +557,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mSyncQueue);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
- TaskPositioner taskPositioner =
+ final TaskPositioner taskPositioner =
new TaskPositioner(mTaskOrganizer, windowDecoration, mDragStartListener);
- CaptionTouchEventListener touchEventListener =
- new CaptionTouchEventListener(
+ final DesktopModeTouchEventListener touchEventListener =
+ new DesktopModeTouchEventListener(
taskInfo, taskPositioner, windowDecoration.getDragDetector());
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
windowDecoration.setDragResizeCallback(taskPositioner);
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 9c2beb9c4b2b..1a38d24a4ab1 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
@@ -56,17 +56,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private View.OnClickListener mOnCaptionButtonClickListener;
private View.OnTouchListener mOnCaptionTouchListener;
private DragResizeCallback mDragResizeCallback;
-
private DragResizeInputListener mDragResizeListener;
+ private final DragDetector mDragDetector;
private RelayoutParams mRelayoutParams = new RelayoutParams();
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
new WindowDecoration.RelayoutResult<>();
private boolean mDesktopActive;
-
- private DragDetector mDragDetector;
-
private AdditionalWindow mHandleMenu;
DesktopModeWindowDecoration(
@@ -121,14 +118,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
- WindowDecorLinearLayout oldRootView = mResult.mRootView;
+ final WindowDecorLinearLayout oldRootView = mResult.mRootView;
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
final WindowContainerTransaction wct = new WindowContainerTransaction();
- int outsetLeftId = R.dimen.freeform_resize_handle;
- int outsetTopId = R.dimen.freeform_resize_handle;
- int outsetRightId = R.dimen.freeform_resize_handle;
- int outsetBottomId = R.dimen.freeform_resize_handle;
+ final int outsetLeftId = R.dimen.freeform_resize_handle;
+ final int outsetTopId = R.dimen.freeform_resize_handle;
+ final int outsetRightId = R.dimen.freeform_resize_handle;
+ final int outsetBottomId = R.dimen.freeform_resize_handle;
mRelayoutParams.reset();
mRelayoutParams.mRunningTaskInfo = taskInfo;
@@ -152,7 +149,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mRelayoutParams.setCaptionPosition(captionLeft, captionTop);
relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
- taskInfo = null; // Clear it just in case we use it accidentally
+ // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
mTaskOrganizer.applyTransaction(wct);
@@ -197,12 +194,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mDragResizeCallback);
}
- int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext()).getScaledTouchSlop();
+ final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
+ .getScaledTouchSlop();
mDragDetector.setTouchSlop(touchSlop);
- int resize_handle = mResult.mRootView.getResources()
+ final int resize_handle = mResult.mRootView.getResources()
.getDimensionPixelSize(R.dimen.freeform_resize_handle);
- int resize_corner = mResult.mRootView.getResources()
+ final int resize_corner = mResult.mRootView.getResources()
.getDimensionPixelSize(R.dimen.freeform_resize_corner);
mDragResizeListener.setGeometry(
mResult.mWidth, mResult.mHeight, resize_handle, resize_corner, touchSlop);
@@ -212,27 +210,27 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* Sets up listeners when a new root view is created.
*/
private void setupRootView() {
- View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
+ final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
caption.setOnTouchListener(mOnCaptionTouchListener);
- View close = caption.findViewById(R.id.close_window);
+ final View close = caption.findViewById(R.id.close_window);
close.setOnClickListener(mOnCaptionButtonClickListener);
- View back = caption.findViewById(R.id.back_button);
+ final View back = caption.findViewById(R.id.back_button);
back.setOnClickListener(mOnCaptionButtonClickListener);
- View handle = caption.findViewById(R.id.caption_handle);
+ final View handle = caption.findViewById(R.id.caption_handle);
handle.setOnTouchListener(mOnCaptionTouchListener);
handle.setOnClickListener(mOnCaptionButtonClickListener);
updateButtonVisibility();
}
private void setupHandleMenu() {
- View menu = mHandleMenu.mWindowViewHost.getView();
- View fullscreen = menu.findViewById(R.id.fullscreen_button);
+ final View menu = mHandleMenu.mWindowViewHost.getView();
+ final View fullscreen = menu.findViewById(R.id.fullscreen_button);
fullscreen.setOnClickListener(mOnCaptionButtonClickListener);
- View desktop = menu.findViewById(R.id.desktop_button);
+ final View desktop = menu.findViewById(R.id.desktop_button);
desktop.setOnClickListener(mOnCaptionButtonClickListener);
- View split = menu.findViewById(R.id.split_screen_button);
+ final View split = menu.findViewById(R.id.split_screen_button);
split.setOnClickListener(mOnCaptionButtonClickListener);
- View more = menu.findViewById(R.id.more_button);
+ final View more = menu.findViewById(R.id.more_button);
more.setOnClickListener(mOnCaptionButtonClickListener);
}
@@ -242,8 +240,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* @param visible whether or not the caption should be visible
*/
private void setCaptionVisibility(boolean visible) {
- int v = visible ? View.VISIBLE : View.GONE;
- View captionView = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
+ final int v = visible ? View.VISIBLE : View.GONE;
+ final View captionView = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
captionView.setVisibility(v);
if (!visible) closeHandleMenu();
}
@@ -264,19 +262,19 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* Show or hide buttons
*/
void setButtonVisibility(boolean visible) {
- int visibility = visible ? View.VISIBLE : View.GONE;
- View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
- View back = caption.findViewById(R.id.back_button);
- View close = caption.findViewById(R.id.close_window);
+ final int visibility = visible ? View.VISIBLE : View.GONE;
+ final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
+ final View back = caption.findViewById(R.id.back_button);
+ final View close = caption.findViewById(R.id.close_window);
back.setVisibility(visibility);
close.setVisibility(visibility);
- int buttonTintColorRes =
+ final int buttonTintColorRes =
mDesktopActive ? R.color.decor_button_dark_color
: R.color.decor_button_light_color;
- ColorStateList buttonTintColor =
+ final ColorStateList buttonTintColor =
caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */);
- View handle = caption.findViewById(R.id.caption_handle);
- VectorDrawable handleBackground = (VectorDrawable) handle.getBackground();
+ final View handle = caption.findViewById(R.id.caption_handle);
+ final VectorDrawable handleBackground = (VectorDrawable) handle.getBackground();
handleBackground.setTintList(buttonTintColor);
caption.getBackground().setTint(visible ? Color.WHITE : Color.TRANSPARENT);
}
@@ -297,12 +295,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* Create and display handle menu window
*/
void createHandleMenu() {
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
final Resources resources = mDecorWindowContext.getResources();
- int x = mRelayoutParams.mCaptionX;
- int y = mRelayoutParams.mCaptionY;
- int width = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionWidthId);
- int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
+ final int x = mRelayoutParams.mCaptionX;
+ final int y = mRelayoutParams.mCaptionY;
+ final int width = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionWidthId);
+ final int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
String namePrefix = "Caption Menu";
mHandleMenu = addWindow(R.layout.desktop_mode_decor_handle_menu, namePrefix, t,
x - mResult.mDecorContainerOffsetX, y - mResult.mDecorContainerOffsetY,
@@ -353,8 +351,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* @return the point of the input in local space
*/
private PointF offsetCaptionLocation(MotionEvent ev) {
- PointF result = new PointF(ev.getX(), ev.getY());
- Point positionInParent = mTaskOrganizer.getRunningTaskInfo(mTaskInfo.taskId)
+ final PointF result = new PointF(ev.getX(), ev.getY());
+ final Point positionInParent = mTaskOrganizer.getRunningTaskInfo(mTaskInfo.taskId)
.positionInParent;
result.offset(-mRelayoutParams.mCaptionX, -mRelayoutParams.mCaptionY);
result.offset(-positionInParent.x, -positionInParent.y);
@@ -370,8 +368,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
private boolean checkEventInCaptionView(MotionEvent ev, int layoutId) {
if (mResult.mRootView == null) return false;
- PointF inputPoint = offsetCaptionLocation(ev);
- View view = mResult.mRootView.findViewById(layoutId);
+ final PointF inputPoint = offsetCaptionLocation(ev);
+ final View view = mResult.mRootView.findViewById(layoutId);
return view != null && view.pointInView(inputPoint.x, inputPoint.y, 0);
}
@@ -389,20 +387,20 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
void checkClickEvent(MotionEvent ev) {
if (mResult.mRootView == null) return;
- View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
- PointF inputPoint = offsetCaptionLocation(ev);
+ final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
+ final PointF inputPoint = offsetCaptionLocation(ev);
if (!isHandleMenuActive()) {
- View handle = caption.findViewById(R.id.caption_handle);
+ final View handle = caption.findViewById(R.id.caption_handle);
clickIfPointInView(inputPoint, handle);
} else {
- View menu = mHandleMenu.mWindowViewHost.getView();
- View fullscreen = menu.findViewById(R.id.fullscreen_button);
+ final View menu = mHandleMenu.mWindowViewHost.getView();
+ final View fullscreen = menu.findViewById(R.id.fullscreen_button);
if (clickIfPointInView(inputPoint, fullscreen)) return;
- View desktop = menu.findViewById(R.id.desktop_button);
+ final View desktop = menu.findViewById(R.id.desktop_button);
if (clickIfPointInView(inputPoint, desktop)) return;
- View split = menu.findViewById(R.id.split_screen_button);
+ final View split = menu.findViewById(R.id.split_screen_button);
if (clickIfPointInView(inputPoint, split)) return;
- View more = menu.findViewById(R.id.more_button);
+ final View more = menu.findViewById(R.id.more_button);
clickIfPointInView(inputPoint, more);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java
new file mode 100644
index 000000000000..aea340464304
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.hardware.input.InputManager;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.transition.Transitions;
+
+/**
+ * Utility class to handle task operations performed on a window decoration.
+ */
+class TaskOperations {
+ private static final String TAG = "TaskOperations";
+
+ private final FreeformTaskTransitionStarter mTransitionStarter;
+ private final Context mContext;
+ private final SyncTransactionQueue mSyncQueue;
+
+ TaskOperations(FreeformTaskTransitionStarter transitionStarter, Context context,
+ SyncTransactionQueue syncQueue) {
+ mTransitionStarter = transitionStarter;
+ mContext = context;
+ mSyncQueue = syncQueue;
+ }
+
+ void injectBackKey() {
+ sendBackEvent(KeyEvent.ACTION_DOWN);
+ sendBackEvent(KeyEvent.ACTION_UP);
+ }
+
+ private void sendBackEvent(int action) {
+ final long when = SystemClock.uptimeMillis();
+ final KeyEvent ev = new KeyEvent(when, when, action, KeyEvent.KEYCODE_BACK,
+ 0 /* repeat */, 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD,
+ 0 /* scancode */, KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
+ InputDevice.SOURCE_KEYBOARD);
+
+ ev.setDisplayId(mContext.getDisplay().getDisplayId());
+ if (!InputManager.getInstance()
+ .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC)) {
+ Log.e(TAG, "Inject input event fail");
+ }
+ }
+
+ void closeTask(WindowContainerToken taskToken) {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.removeTask(taskToken);
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mTransitionStarter.startRemoveTransition(wct);
+ } else {
+ mSyncQueue.queue(wct);
+ }
+ }
+
+ void minimizeTask(WindowContainerToken taskToken) {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reorder(taskToken, false);
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mTransitionStarter.startMinimizedModeTransition(wct);
+ } else {
+ mSyncQueue.queue(wct);
+ }
+ }
+
+ void maximizeTask(RunningTaskInfo taskInfo) {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ int targetWindowingMode = taskInfo.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
+ ? WINDOWING_MODE_FULLSCREEN : WINDOWING_MODE_FREEFORM;
+ int displayWindowingMode =
+ taskInfo.configuration.windowConfiguration.getDisplayWindowingMode();
+ wct.setWindowingMode(taskInfo.token,
+ targetWindowingMode == displayWindowingMode
+ ? WINDOWING_MODE_UNDEFINED : targetWindowingMode);
+ if (targetWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ wct.setBounds(taskInfo.token, null);
+ }
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mTransitionStarter.startWindowingModeTransition(targetWindowingMode, wct);
+ } else {
+ mSyncQueue.queue(wct);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
index a49a300995e6..20631f85453f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
@@ -47,6 +47,10 @@ class TaskPositioner implements DragResizeCallback {
private int mCtrlType;
private DragStartListener mDragStartListener;
+ TaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration) {
+ this(taskOrganizer, windowDecoration, dragStartListener -> {});
+ }
+
TaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
DragStartListener dragStartListener) {
mTaskOrganizer = taskOrganizer;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
index 35cc16852b9f..7997a7ed7f7f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
@@ -334,6 +334,41 @@ public class DesktopModeControllerTest extends ShellTestCase {
}
@Test
+ public void testGetVisibleTaskCount_noTasks_returnsZero() {
+ assertThat(mController.getVisibleTaskCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void testGetVisibleTaskCount_twoTasks_bothVisible_returnsTwo() {
+ RunningTaskInfo task1 = createFreeformTask();
+ mDesktopModeTaskRepository.addActiveTask(task1.taskId);
+ mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */);
+
+ RunningTaskInfo task2 = createFreeformTask();
+ mDesktopModeTaskRepository.addActiveTask(task2.taskId);
+ mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */);
+
+ assertThat(mController.getVisibleTaskCount()).isEqualTo(2);
+ }
+
+ @Test
+ public void testGetVisibleTaskCount_twoTasks_oneVisible_returnsOne() {
+ RunningTaskInfo task1 = createFreeformTask();
+ mDesktopModeTaskRepository.addActiveTask(task1.taskId);
+ mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */);
+
+ RunningTaskInfo task2 = createFreeformTask();
+ mDesktopModeTaskRepository.addActiveTask(task2.taskId);
+ mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, false /* visible */);
+
+ assertThat(mController.getVisibleTaskCount()).isEqualTo(1);
+ }
+
+ @Test
public void testHandleTransitionRequest_desktopModeNotActive_returnsNull() {
when(DesktopModeStatus.isActive(any())).thenReturn(false);
WindowContainerTransaction wct = mController.handleRequest(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
index 1e43a5983821..45cb3a062cc5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -141,6 +141,36 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
}
@Test
+ fun getVisibleTaskCount() {
+ // No tasks, count is 0
+ assertThat(repo.getVisibleTaskCount()).isEqualTo(0)
+
+ // New task increments count to 1
+ repo.updateVisibleFreeformTasks(taskId = 1, visible = true)
+ assertThat(repo.getVisibleTaskCount()).isEqualTo(1)
+
+ // Visibility update to same task does not increase count
+ repo.updateVisibleFreeformTasks(taskId = 1, visible = true)
+ assertThat(repo.getVisibleTaskCount()).isEqualTo(1)
+
+ // Second task visible increments count
+ repo.updateVisibleFreeformTasks(taskId = 2, visible = true)
+ assertThat(repo.getVisibleTaskCount()).isEqualTo(2)
+
+ // Hiding a task decrements count
+ repo.updateVisibleFreeformTasks(taskId = 1, visible = false)
+ assertThat(repo.getVisibleTaskCount()).isEqualTo(1)
+
+ // Hiding all tasks leaves count at 0
+ repo.updateVisibleFreeformTasks(taskId = 2, visible = false)
+ assertThat(repo.getVisibleTaskCount()).isEqualTo(0)
+
+ // Hiding a not existing task, count remains at 0
+ repo.updateVisibleFreeformTasks(taskId = 999, visible = false)
+ assertThat(repo.getVisibleTaskCount()).isEqualTo(0)
+ }
+
+ @Test
fun addOrMoveFreeformTaskToTop_didNotExist_addsToTop() {
repo.addOrMoveFreeformTaskToTop(5)
repo.addOrMoveFreeformTaskToTop(6)
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 4011d080564d..f16beeed57a3 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
@@ -197,6 +197,27 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun getVisibleTaskCount_noTasks_returnsZero() {
+ assertThat(controller.getVisibleTaskCount()).isEqualTo(0)
+ }
+
+ @Test
+ fun getVisibleTaskCount_twoTasks_bothVisible_returnsTwo() {
+ setUpHomeTask()
+ setUpFreeformTask().also(::markTaskVisible)
+ setUpFreeformTask().also(::markTaskVisible)
+ assertThat(controller.getVisibleTaskCount()).isEqualTo(2)
+ }
+
+ @Test
+ fun getVisibleTaskCount_twoTasks_oneVisible_returnsOne() {
+ setUpHomeTask()
+ setUpFreeformTask().also(::markTaskVisible)
+ setUpFreeformTask().also(::markTaskHidden)
+ assertThat(controller.getVisibleTaskCount()).isEqualTo(1)
+ }
+
+ @Test
fun moveToDesktop() {
val task = setUpFullscreenTask()
controller.moveToDesktop(task)
@@ -224,7 +245,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
assertThat(hierarchyOps).hasSize(3)
assertReorderSequence(homeTask, freeformTask, fullscreenTask)
assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index 262e4290ef44..298d0a624869 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -64,7 +64,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
initializeMockResources();
mPipBoundsState = new PipBoundsState(mContext);
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
- new PipSnapAlgorithm(), new PipKeepClearAlgorithm() {});
+ new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {});
mPipBoundsState.setDisplayLayout(
new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true));
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 90880772b25d..17e7d74c57e0 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
@@ -98,7 +98,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mPipBoundsState = new PipBoundsState(mContext);
mPipTransitionState = new PipTransitionState();
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
- new PipSnapAlgorithm(), new PipKeepClearAlgorithm() {});
+ new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {});
mMainExecutor = new TestShellExecutor();
mPipTaskOrganizer = new PipTaskOrganizer(mContext,
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index 3bd2ae76ebfd..c1993b25030b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -37,7 +37,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipKeepClearAlgorithm;
+import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
@@ -90,8 +90,8 @@ public class PipResizeGestureHandlerTest extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mPipBoundsState = new PipBoundsState(mContext);
final PipSnapAlgorithm pipSnapAlgorithm = new PipSnapAlgorithm();
- final PipKeepClearAlgorithm pipKeepClearAlgorithm =
- new PipKeepClearAlgorithm() {};
+ final PipKeepClearAlgorithmInterface pipKeepClearAlgorithm =
+ new PipKeepClearAlgorithmInterface() {};
final PipBoundsAlgorithm pipBoundsAlgorithm = new PipBoundsAlgorithm(mContext,
mPipBoundsState, pipSnapAlgorithm, pipKeepClearAlgorithm);
final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 474d6aaf4623..8ad2932b69e4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -34,7 +34,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipKeepClearAlgorithm;
+import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
@@ -106,7 +106,7 @@ public class PipTouchHandlerTest extends ShellTestCase {
mPipBoundsState = new PipBoundsState(mContext);
mPipSnapAlgorithm = new PipSnapAlgorithm();
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm,
- new PipKeepClearAlgorithm() {});
+ new PipKeepClearAlgorithmInterface() {});
PipMotionHelper pipMotionHelper = new PipMotionHelper(mContext, mPipBoundsState,
mPipTaskOrganizer, mPhonePipMenuController, mPipSnapAlgorithm,
mMockPipTransitionController, mFloatingContentCoordinator);
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java
index f6914efd6d83..23d6e34db4f1 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java
@@ -22,8 +22,8 @@ import static com.android.server.backup.encryption.protos.nano.ChunksMetadataPro
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java
index 096b2da10c98..bfc5d0dca3ff 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java
@@ -18,9 +18,9 @@ package com.android.server.backup.encryption.tasks;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java
index fa4fef50ac1a..222b88221ba2 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java
@@ -19,8 +19,8 @@ package com.android.server.backup.encryption.tasks;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertFalse;
@@ -41,13 +41,6 @@ import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
import com.android.server.backup.testing.CryptoTestUtils;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import javax.crypto.SecretKey;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -59,6 +52,14 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.crypto.SecretKey;
+
+
@RunWith(RobolectricTestRunner.class)
public class EncryptedKvBackupTaskTest {
private static final boolean INCREMENTAL = true;
diff --git a/packages/PackageInstaller/res/values-ky/strings.xml b/packages/PackageInstaller/res/values-ky/strings.xml
index 2684fd969880..0f19fe81b379 100644
--- a/packages/PackageInstaller/res/values-ky/strings.xml
+++ b/packages/PackageInstaller/res/values-ky/strings.xml
@@ -88,7 +88,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Планшетиңиз жана жеке дайын-даректериңиз белгисиз колдонмолордон зыян тартып калышы мүмкүн. Бул колдонмону орнотуп, аны пайдалануудан улам планшетиңизге кандайдыр бир зыян келтирилсе же дайын-даректериңизды жоготуп алсаңыз, өзүңүз жооптуу болосуз."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Сыналгыңыз жана жеке дайын-даректериңиз белгисиз колдонмолордон зыян тартып калышы мүмкүн. Бул колдонмону орнотуп, аны пайдалануудан улам сыналгыңызга кандайдыр бир зыян келтирилсе же дайын-даректериңизды жоготуп алсаңыз, өзүңүз жооптуу болосуз."</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Улантуу"</string>
- <string name="external_sources_settings" msgid="4046964413071713807">"Жөндөөлөр"</string>
+ <string name="external_sources_settings" msgid="4046964413071713807">"Параметрлер"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Тагынма колдонмолорду орнотуу/чыгаруу"</string>
<string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Колдонмолорду орноткучтун билдирмелери"</string>
<string name="notification_installation_success_message" msgid="6450467996056038442">"Ийгиликтүү орнотулду"</string>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index 468a97630e19..15920940140c 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -59,6 +59,18 @@ public class IllustrationPreference extends Preference {
private Uri mImageUri;
private Drawable mImageDrawable;
private View mMiddleGroundView;
+ private OnBindListener mOnBindListener;
+
+ /**
+ * Interface to listen in on when {@link #onBindViewHolder(PreferenceViewHolder)} occurs.
+ */
+ public interface OnBindListener {
+ /**
+ * Called when when {@link #onBindViewHolder(PreferenceViewHolder)} occurs.
+ * @param animationView the animation view for this preference.
+ */
+ void onBind(LottieAnimationView animationView);
+ }
private final Animatable2.AnimationCallback mAnimationCallback =
new Animatable2.AnimationCallback() {
@@ -133,6 +145,17 @@ public class IllustrationPreference extends Preference {
if (IS_ENABLED_LOTTIE_ADAPTIVE_COLOR) {
ColorUtils.applyDynamicColors(getContext(), illustrationView);
}
+
+ if (mOnBindListener != null) {
+ mOnBindListener.onBind(illustrationView);
+ }
+ }
+
+ /**
+ * Sets a listener to be notified when the views are binded.
+ */
+ public void setOnBindListener(OnBindListener listener) {
+ mOnBindListener = listener;
}
/**
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ky/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ky/strings.xml
index f4a893a1cd83..bcd6784bc8f8 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ky/strings.xml
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ky/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="settings_label" msgid="5948970810295631236">"Жөндөөлөр"</string>
+ <string name="settings_label" msgid="5948970810295631236">"Параметрлер"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
index 66aaa56b201a..b69fd5370e3b 100644
--- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
@@ -247,7 +247,7 @@
</string-array>
<string-array name="track_frame_time_entries">
<item msgid="634406443901014984">"關閉"</item>
- <item msgid="1288760936356000927">"在螢幕上以列顯示"</item>
+ <item msgid="1288760936356000927">"在螢幕上顯示長條圖"</item>
<item msgid="5023908510820531131">"在「<xliff:g id="AS_TYPED_COMMAND">adb shell dumpsys gfxinfo</xliff:g>」指令中"</item>
</string-array>
<string-array name="debug_hw_overdraw_entries">
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index f5575170e92a..50d292192727 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -351,7 +351,7 @@
<string name="debug_app_set" msgid="6599535090477753651">"偵錯應用程式:<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="select_application" msgid="2543228890535466325">"選取應用程式"</string>
<string name="no_application" msgid="9038334538870247690">"無"</string>
- <string name="wait_for_debugger" msgid="7461199843335409809">"等待偵錯程式"</string>
+ <string name="wait_for_debugger" msgid="7461199843335409809">"等待偵錯工具"</string>
<string name="wait_for_debugger_summary" msgid="6846330006113363286">"執行受偵錯的應用程式之前,先等待偵錯程序附加完成"</string>
<string name="debug_input_category" msgid="7349460906970849771">"輸入"</string>
<string name="debug_drawing_category" msgid="5066171112313666619">"繪圖"</string>
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index 1f2297ba3a0c..fc2bf0a9bd93 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -21,10 +21,10 @@ import static android.os.UserHandle.MU_ENABLED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
index 95f7ef41b10b..508dffc2fa21 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
@@ -18,7 +18,7 @@ package com.android.settingslib.net;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
index f28572f5f71d..cf07c6bb7cb9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
@@ -22,7 +22,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import android.app.Activity;
@@ -143,7 +143,7 @@ public class EditUserInfoControllerTest {
dialog.show();
dialog.cancel();
- verifyZeroInteractions(successCallback);
+ verifyNoInteractions(successCallback);
verify(cancelCallback, times(1))
.run();
}
@@ -159,7 +159,7 @@ public class EditUserInfoControllerTest {
dialog.show();
dialog.getButton(Dialog.BUTTON_NEGATIVE).performClick();
- verifyZeroInteractions(successCallback);
+ verifyNoInteractions(successCallback);
verify(cancelCallback, times(1))
.run();
}
@@ -180,7 +180,7 @@ public class EditUserInfoControllerTest {
verify(successCallback, times(1))
.accept("test", oldUserIcon);
- verifyZeroInteractions(cancelCallback);
+ verifyNoInteractions(cancelCallback);
}
@Test
@@ -198,7 +198,7 @@ public class EditUserInfoControllerTest {
verify(successCallback, times(1))
.accept("test", null);
- verifyZeroInteractions(cancelCallback);
+ verifyNoInteractions(cancelCallback);
}
@Test
@@ -219,7 +219,7 @@ public class EditUserInfoControllerTest {
verify(successCallback, times(1))
.accept(expectedNewName, mCurrentIcon);
- verifyZeroInteractions(cancelCallback);
+ verifyNoInteractions(cancelCallback);
}
@Test
@@ -238,7 +238,7 @@ public class EditUserInfoControllerTest {
verify(successCallback, times(1))
.accept("test", newPhoto);
- verifyZeroInteractions(cancelCallback);
+ verifyNoInteractions(cancelCallback);
}
@Test
@@ -257,7 +257,7 @@ public class EditUserInfoControllerTest {
verify(successCallback, times(1))
.accept("test", newPhoto);
- verifyZeroInteractions(cancelCallback);
+ verifyNoInteractions(cancelCallback);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
index 29549d9a7fa7..103512d4a28a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
@@ -61,6 +61,8 @@ public class IllustrationPreferenceTest {
private PreferenceViewHolder mViewHolder;
private FrameLayout mMiddleGroundLayout;
private final Context mContext = ApplicationProvider.getApplicationContext();
+ private IllustrationPreference.OnBindListener mOnBindListener;
+ private LottieAnimationView mOnBindListenerAnimationView;
@Before
public void setUp() {
@@ -82,6 +84,12 @@ public class IllustrationPreferenceTest {
final AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
mPreference = new IllustrationPreference(mContext, attributeSet);
+ mOnBindListener = new IllustrationPreference.OnBindListener() {
+ @Override
+ public void onBind(LottieAnimationView animationView) {
+ mOnBindListenerAnimationView = animationView;
+ }
+ };
}
@Test
@@ -186,4 +194,25 @@ public class IllustrationPreferenceTest {
assertThat(mBackgroundView.getMaxHeight()).isEqualTo(restrictedHeight);
assertThat(mAnimationView.getMaxHeight()).isEqualTo(restrictedHeight);
}
+
+ @Test
+ public void setOnBindListener_isNotified() {
+ mOnBindListenerAnimationView = null;
+ mPreference.setOnBindListener(mOnBindListener);
+
+ mPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mOnBindListenerAnimationView).isNotNull();
+ assertThat(mOnBindListenerAnimationView).isEqualTo(mAnimationView);
+ }
+
+ @Test
+ public void setOnBindListener_notNotified() {
+ mOnBindListenerAnimationView = null;
+ mPreference.setOnBindListener(null);
+
+ mPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mOnBindListenerAnimationView).isNull();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/UpdatableListPreferenceDialogFragmentTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/UpdatableListPreferenceDialogFragmentTest.java
index 0b3495def21f..ca0aa0d7b900 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/UpdatableListPreferenceDialogFragmentTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/UpdatableListPreferenceDialogFragmentTest.java
@@ -56,7 +56,7 @@ public class UpdatableListPreferenceDialogFragmentTest {
mUpdatableListPrefDlgFragment = spy(UpdatableListPreferenceDialogFragment
.newInstance(KEY, MetricsProto.MetricsEvent.DIALOG_SWITCH_A2DP_DEVICES));
- mEntries = spy(new ArrayList<>());
+ mEntries = new ArrayList<>();
mUpdatableListPrefDlgFragment.setEntries(mEntries);
mUpdatableListPrefDlgFragment
.setMetricsCategory(mUpdatableListPrefDlgFragment.getArguments());
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index e148d4f547a4..252915703511 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -271,6 +271,7 @@ android_library {
"LowLightDreamLib",
"motion_tool_lib",
"androidx.core_core-animation-testing-nodeps",
+ "androidx.compose.ui_ui",
],
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 810dd33ae17a..c685d62cbf8e 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -292,7 +292,7 @@
<queries>
<intent>
- <action android:name="android.intent.action.NOTES" />
+ <action android:name="android.intent.action.CREATE_NOTE" />
</intent>
</queries>
@@ -411,7 +411,6 @@
<service android:name=".screenshot.ScreenshotCrossProfileService"
android:permission="com.android.systemui.permission.SELF"
- android:process=":screenshot_cross_profile"
android:exported="false" />
<service android:name=".screenrecord.RecordingService" />
@@ -664,6 +663,20 @@
android:excludeFromRecents="true"
android:exported="true" />
+ <!-- started from Telecomm(CallsManager) -->
+ <activity
+ android:name=".telephony.ui.activity.SwitchToManagedProfileForCallActivity"
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:finishOnCloseSystemDialogs="true"
+ android:permission="android.permission.MODIFY_PHONE_STATE"
+ android:theme="@style/Theme.SystemUI.Dialog.Alert">
+ <intent-filter>
+ <action android:name="android.telecom.action.SHOW_SWITCH_TO_WORK_PROFILE_FOR_CALL_DIALOG" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<!-- platform logo easter egg activity -->
<activity
android:name=".DessertCase"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index a450d3af334b..a3ed0856c60a 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -237,13 +237,17 @@ constructor(
openedDialogs.firstOrNull {
it.dialog.window.decorView.viewRootImpl == controller.viewRoot
}
- val animateFrom =
+ val controller =
animatedParent?.dialogContentWithBackground?.let {
Controller.fromView(it, controller.cuj)
}
?: controller
- if (animatedParent == null && animateFrom !is LaunchableView) {
+ if (
+ animatedParent == null &&
+ controller is ViewDialogLaunchAnimatorController &&
+ controller.source !is LaunchableView
+ ) {
// Make sure the View we launch from implements LaunchableView to avoid visibility
// issues. Given that we don't own dialog decorViews so we can't enforce it for launches
// from a dialog.
@@ -272,7 +276,7 @@ constructor(
launchAnimator,
callback,
interactionJankMonitor,
- animateFrom,
+ controller,
onDialogDismissed = { openedDialogs.remove(it) },
dialog = dialog,
animateBackgroundBoundsChange,
@@ -791,13 +795,13 @@ private class AnimatedDialog(
// Move the drawing of the source in the overlay of this dialog, then animate. We trigger a
// one-off synchronization to make sure that this is done in sync between the two different
// windows.
+ controller.startDrawingInOverlayOf(decorView)
synchronizeNextDraw(
then = {
isSourceDrawnInDialog = true
maybeStartLaunchAnimation()
}
)
- controller.startDrawingInOverlayOf(decorView)
}
/**
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 0028d13ffd5e..dfac02d99c4d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -195,14 +195,16 @@ open class GhostedViewLaunchAnimatorController @JvmOverloads constructor(
backgroundDrawable = WrappedDrawable(background)
backgroundView?.background = backgroundDrawable
+ // Delay the calls to `ghostedView.setVisibility()` during the animation. This must be
+ // called before `GhostView.addGhost()` is called because the latter will change the
+ // *transition* visibility, which won't be blocked and will affect the normal View
+ // visibility that is saved by `setShouldBlockVisibilityChanges()` for a later restoration.
+ (ghostedView as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
+
// Create a ghost of the view that will be moving and fading out. This allows to fade out
// the content before fading out the background.
ghostView = GhostView.addGhost(ghostedView, launchContainer)
- // The ghost was just created, so ghostedView is currently invisible. We need to make sure
- // that it stays invisible as long as we are animating.
- (ghostedView as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
-
val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX
matrix.getValues(initialGhostViewMatrixValues)
@@ -297,14 +299,19 @@ open class GhostedViewLaunchAnimatorController @JvmOverloads constructor(
backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha
GhostView.removeGhost(ghostedView)
- (ghostedView as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
launchContainerOverlay.remove(backgroundView)
- // Make sure that the view is considered VISIBLE by accessibility by first making it
- // INVISIBLE then VISIBLE (see b/204944038#comment17 for more info).
- ghostedView.visibility = View.INVISIBLE
- ghostedView.visibility = View.VISIBLE
- ghostedView.invalidate()
+ if (ghostedView is LaunchableView) {
+ // Restore the ghosted view visibility.
+ ghostedView.setShouldBlockVisibilityChanges(false)
+ } else {
+ // Make the ghosted view visible. We ensure that the view is considered VISIBLE by
+ // accessibility by first making it INVISIBLE then VISIBLE (see b/204944038#comment17
+ // for more info).
+ ghostedView.visibility = View.INVISIBLE
+ ghostedView.visibility = View.VISIBLE
+ ghostedView.invalidate()
+ }
}
companion object {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
index 67b59e0e9928..774255be4007 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
@@ -21,15 +21,19 @@ import android.view.View
/** A view that can expand/launch into an app or a dialog. */
interface LaunchableView {
/**
- * Set whether this view should block/postpone all visibility changes. This ensures that this
- * view:
+ * Set whether this view should block/postpone all calls to [View.setVisibility]. This ensures
+ * that this view:
* - remains invisible during the launch animation given that it is ghosted and already drawn
* somewhere else.
* - remains invisible as long as a dialog expanded from it is shown.
* - restores its expected visibility once the dialog expanded from it is dismissed.
*
- * Note that when this is set to true, both the [normal][android.view.View.setVisibility] and
- * [transition][android.view.View.setTransitionVisibility] visibility changes must be blocked.
+ * When `setShouldBlockVisibilityChanges(false)` is called, then visibility of the View should
+ * be restored to its expected value, i.e. it should have the visibility of the last call to
+ * `View.setVisibility()` that was made after `setShouldBlockVisibilityChanges(true)`, if any,
+ * or the original view visibility otherwise.
+ *
+ * Note that calls to [View.setTransitionVisibility] shouldn't be blocked.
*
* @param block whether we should block/postpone all calls to `setVisibility` and
* `setTransitionVisibility`.
@@ -46,27 +50,31 @@ class LaunchableViewDelegate(
* super.setVisibility(visibility).
*/
private val superSetVisibility: (Int) -> Unit,
-
- /**
- * The lambda that should set the actual transition visibility of [view], usually by calling
- * super.setTransitionVisibility(visibility).
- */
- private val superSetTransitionVisibility: (Int) -> Unit,
-) {
+) : LaunchableView {
private var blockVisibilityChanges = false
private var lastVisibility = view.visibility
/** Call this when [LaunchableView.setShouldBlockVisibilityChanges] is called. */
- fun setShouldBlockVisibilityChanges(block: Boolean) {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {
if (block == blockVisibilityChanges) {
return
}
blockVisibilityChanges = block
if (block) {
+ // Save the current visibility for later.
lastVisibility = view.visibility
} else {
- superSetVisibility(lastVisibility)
+ // Restore the visibility. To avoid accessibility issues, we change the visibility twice
+ // which makes sure that we trigger a visibility flag change (see b/204944038#comment17
+ // for more info).
+ if (lastVisibility == View.VISIBLE) {
+ superSetVisibility(View.INVISIBLE)
+ superSetVisibility(View.VISIBLE)
+ } else {
+ superSetVisibility(View.VISIBLE)
+ superSetVisibility(lastVisibility)
+ }
}
}
@@ -79,16 +87,4 @@ class LaunchableViewDelegate(
superSetVisibility(visibility)
}
-
- /** Call this when [View.setTransitionVisibility] is called. */
- fun setTransitionVisibility(visibility: Int) {
- if (blockVisibilityChanges) {
- // View.setTransitionVisibility just sets the visibility flag, so we don't have to save
- // the transition visibility separately from the normal visibility.
- lastVisibility = visibility
- return
- }
-
- superSetTransitionVisibility(visibility)
- }
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
index 964ef8c88098..9257f99efe96 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
@@ -25,7 +25,7 @@ import com.android.internal.jank.InteractionJankMonitor
/** A [DialogLaunchAnimator.Controller] that can animate a [View] from/to a dialog. */
class ViewDialogLaunchAnimatorController
internal constructor(
- private val source: View,
+ internal val source: View,
override val cuj: DialogCuj?,
) : DialogLaunchAnimator.Controller {
override val viewRoot: ViewRootImpl?
@@ -34,23 +34,29 @@ internal constructor(
override val sourceIdentity: Any = source
override fun startDrawingInOverlayOf(viewGroup: ViewGroup) {
+ // Delay the calls to `source.setVisibility()` during the animation. This must be called
+ // before `GhostView.addGhost()` is called because the latter will change the *transition*
+ // visibility, which won't be blocked and will affect the normal View visibility that is
+ // saved by `setShouldBlockVisibilityChanges()` for a later restoration.
+ (source as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
+
// Create a temporary ghost of the source (which will make it invisible) and add it
// to the host dialog.
GhostView.addGhost(source, viewGroup)
-
- // The ghost of the source was just created, so the source is currently invisible.
- // We need to make sure that it stays invisible as long as the dialog is shown or
- // animating.
- (source as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
}
override fun stopDrawingInOverlay() {
// Note: here we should remove the ghost from the overlay, but in practice this is
- // already done by the launch controllers created below.
-
- // Make sure we allow the source to change its visibility again.
- (source as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
- source.visibility = View.VISIBLE
+ // already done by the launch controller created below.
+
+ if (source is LaunchableView) {
+ // Make sure we allow the source to change its visibility again and restore its previous
+ // value.
+ source.setShouldBlockVisibilityChanges(false)
+ } else {
+ // We made the source invisible earlier, so let's make it visible again.
+ source.visibility = View.VISIBLE
+ }
}
override fun createLaunchController(): LaunchAnimator.Controller {
@@ -67,10 +73,14 @@ internal constructor(
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
- // We hide the source when the dialog is showing. We will make this view
- // visible again when dismissing the dialog. This does nothing if the source
- // implements [LaunchableView], as it's already INVISIBLE in that case.
- source.visibility = View.INVISIBLE
+ // At this point the view visibility is restored by the delegate, so we delay the
+ // visibility changes again and make it invisible while the dialog is shown.
+ if (source is LaunchableView) {
+ source.setShouldBlockVisibilityChanges(true)
+ source.setTransitionVisibility(View.INVISIBLE)
+ } else {
+ source.visibility = View.INVISIBLE
+ }
}
}
}
@@ -90,13 +100,15 @@ internal constructor(
}
override fun onExitAnimationCancelled() {
- // Make sure we allow the source to change its visibility again.
- (source as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
-
- // If the view is invisible it's probably because of us, so we make it visible
- // again.
- if (source.visibility == View.INVISIBLE) {
- source.visibility = View.VISIBLE
+ if (source is LaunchableView) {
+ // Make sure we allow the source to change its visibility again.
+ source.setShouldBlockVisibilityChanges(false)
+ } else {
+ // If the view is invisible it's probably because of us, so we make it visible
+ // again.
+ if (source.visibility == View.INVISIBLE) {
+ source.visibility = View.VISIBLE
+ }
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt
index 67159512a701..79bc2f432ded 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt
@@ -57,7 +57,7 @@ data class TurbulenceNoiseAnimationConfig(
val onAnimationEnd: Runnable? = null
) {
companion object {
- const val DEFAULT_MAX_DURATION_IN_MILLIS = 7500f
+ const val DEFAULT_MAX_DURATION_IN_MILLIS = 30_000f // Max 30 sec
const val DEFAULT_EASING_DURATION_IN_MILLIS = 750f
const val DEFAULT_LUMINOSITY_MULTIPLIER = 1f
const val DEFAULT_NOISE_GRID_COUNT = 1.2f
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index 259f0ed5c7a1..4e96ddabfda6 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -41,6 +41,7 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.movableContentOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCompositionContext
@@ -73,6 +74,7 @@ import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewTreeLifecycleOwner
import androidx.lifecycle.ViewTreeViewModelStoreOwner
+import com.android.compose.runtime.movableContentOf
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.LaunchAnimator
import kotlin.math.max
@@ -170,25 +172,25 @@ fun Expandable(
val contentColor = controller.contentColor
val shape = controller.shape
- // TODO(b/230830644): Use movableContentOf to preserve the content state instead once the
- // Compose libraries have been updated and include aosp/2163631.
val wrappedContent =
- @Composable { controller: ExpandableController ->
- CompositionLocalProvider(
- LocalContentColor provides contentColor,
- ) {
- // We make sure that the content itself (wrapped by the background) is at least
- // 40.dp, which is the same as the M3 buttons. This applies even if onClick is
- // null, to make it easier to write expandables that are sometimes clickable and
- // sometimes not. There shouldn't be any Expandable smaller than 40dp because if
- // the expandable is not clickable directly, then something in its content should
- // be (and with a size >= 40dp).
- val minSize = 40.dp
- Box(
- Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
- contentAlignment = Alignment.Center,
+ remember(content) {
+ movableContentOf { expandable: Expandable ->
+ CompositionLocalProvider(
+ LocalContentColor provides contentColor,
) {
- content(controller.expandable)
+ // We make sure that the content itself (wrapped by the background) is at least
+ // 40.dp, which is the same as the M3 buttons. This applies even if onClick is
+ // null, to make it easier to write expandables that are sometimes clickable and
+ // sometimes not. There shouldn't be any Expandable smaller than 40dp because if
+ // the expandable is not clickable directly, then something in its content
+ // should be (and with a size >= 40dp).
+ val minSize = 40.dp
+ Box(
+ Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
+ contentAlignment = Alignment.Center,
+ ) {
+ content(expandable)
+ }
}
}
}
@@ -270,7 +272,7 @@ fun Expandable(
.onGloballyPositioned {
controller.boundsInComposeViewRoot.value = it.boundsInRoot()
}
- ) { wrappedContent(controller) }
+ ) { wrappedContent(controller.expandable) }
}
else -> {
val clickModifier =
@@ -301,7 +303,7 @@ fun Expandable(
controller.boundsInComposeViewRoot.value = it.boundsInRoot()
},
) {
- wrappedContent(controller)
+ wrappedContent(controller.expandable)
}
}
}
@@ -315,7 +317,7 @@ private fun AnimatedContentInOverlay(
animatorState: State<LaunchAnimator.State?>,
overlay: ViewGroupOverlay,
controller: ExpandableControllerImpl,
- content: @Composable (ExpandableController) -> Unit,
+ content: @Composable (Expandable) -> Unit,
composeViewRoot: View,
onOverlayComposeViewChanged: (View?) -> Unit,
density: Density,
@@ -370,7 +372,7 @@ private fun AnimatedContentInOverlay(
// We center the content in the expanding container.
contentAlignment = Alignment.Center,
) {
- Box(contentModifier) { content(controller) }
+ Box(contentModifier) { content(controller.expandable) }
}
}
}
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
index 6e728ce7248f..e253fb925ceb 100644
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -17,13 +17,21 @@
package com.android.systemui.compose
+import android.content.Context
+import android.view.View
import androidx.activity.ComponentActivity
+import androidx.lifecycle.LifecycleOwner
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
/** The Compose facade, when Compose is *not* available. */
object ComposeFacade : BaseComposeFacade {
override fun isComposeAvailable(): Boolean = false
+ override fun composeInitializer(): ComposeInitializer {
+ throwComposeUnavailableError()
+ }
+
override fun setPeopleSpaceActivityContent(
activity: ComponentActivity,
viewModel: PeopleViewModel,
@@ -32,7 +40,15 @@ object ComposeFacade : BaseComposeFacade {
throwComposeUnavailableError()
}
- private fun throwComposeUnavailableError() {
+ override fun createFooterActionsView(
+ context: Context,
+ viewModel: FooterActionsViewModel,
+ qsVisibilityLifecycleOwner: LifecycleOwner
+ ): View {
+ throwComposeUnavailableError()
+ }
+
+ private fun throwComposeUnavailableError(): Nothing {
error(
"Compose is not available. Make sure to check isComposeAvailable() before calling any" +
" other function on ComposeFacade."
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
index 6991ff82c2d1..1ea18fec4abe 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -16,16 +16,24 @@
package com.android.systemui.compose
+import android.content.Context
+import android.view.View
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
+import androidx.compose.ui.platform.ComposeView
+import androidx.lifecycle.LifecycleOwner
import com.android.compose.theme.PlatformTheme
import com.android.systemui.people.ui.compose.PeopleScreen
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
+import com.android.systemui.qs.footer.ui.compose.FooterActions
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
/** The Compose facade, when Compose is available. */
object ComposeFacade : BaseComposeFacade {
override fun isComposeAvailable(): Boolean = true
+ override fun composeInitializer(): ComposeInitializer = ComposeInitializerImpl
+
override fun setPeopleSpaceActivityContent(
activity: ComponentActivity,
viewModel: PeopleViewModel,
@@ -33,4 +41,14 @@ object ComposeFacade : BaseComposeFacade {
) {
activity.setContent { PlatformTheme { PeopleScreen(viewModel, onResult) } }
}
+
+ override fun createFooterActionsView(
+ context: Context,
+ viewModel: FooterActionsViewModel,
+ qsVisibilityLifecycleOwner: LifecycleOwner,
+ ): View {
+ return ComposeView(context).apply {
+ setContent { PlatformTheme { FooterActions(viewModel, qsVisibilityLifecycleOwner) } }
+ }
+ }
}
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt
new file mode 100644
index 000000000000..772c8918fd2d
--- /dev/null
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.compose
+
+import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.ViewTreeLifecycleOwner
+import androidx.savedstate.SavedStateRegistry
+import androidx.savedstate.SavedStateRegistryController
+import androidx.savedstate.SavedStateRegistryOwner
+import com.android.compose.animation.ViewTreeSavedStateRegistryOwner
+import com.android.systemui.lifecycle.ViewLifecycleOwner
+
+internal object ComposeInitializerImpl : ComposeInitializer {
+ override fun onAttachedToWindow(root: View) {
+ if (ViewTreeLifecycleOwner.get(root) != null) {
+ error("root $root already has a LifecycleOwner")
+ }
+
+ val parent = root.parent
+ if (parent is View && parent.id != android.R.id.content) {
+ error(
+ "ComposeInitializer.onAttachedToWindow(View) must be called on the content child." +
+ "Outside of activities and dialogs, this is usually the top-most View of a " +
+ "window."
+ )
+ }
+
+ // The lifecycle owner, which is STARTED when [root] is visible and RESUMED when [root] is
+ // both visible and focused.
+ val lifecycleOwner = ViewLifecycleOwner(root)
+
+ // We create a trivial implementation of [SavedStateRegistryOwner] that does not do any save
+ // or restore because SystemUI process is always running and top-level windows using this
+ // initializer are created once, when the process is started.
+ val savedStateRegistryOwner =
+ object : SavedStateRegistryOwner {
+ private val savedStateRegistry =
+ SavedStateRegistryController.create(this).apply { performRestore(null) }
+
+ override fun getLifecycle(): Lifecycle = lifecycleOwner.lifecycle
+
+ override fun getSavedStateRegistry(): SavedStateRegistry {
+ return savedStateRegistry.savedStateRegistry
+ }
+ }
+
+ // We must call [ViewLifecycleOwner.onCreate] after creating the [SavedStateRegistryOwner]
+ // because `onCreate` might move the lifecycle state to STARTED which will make
+ // [SavedStateRegistryController.performRestore] throw.
+ lifecycleOwner.onCreate()
+
+ // Set the owners on the root. They will be reused by any ComposeView inside the root
+ // hierarchy.
+ ViewTreeLifecycleOwner.set(root, lifecycleOwner)
+ ViewTreeSavedStateRegistryOwner.set(root, savedStateRegistryOwner)
+ }
+
+ override fun onDetachedFromWindow(root: View) {
+ (ViewTreeLifecycleOwner.get(root) as ViewLifecycleOwner).onDestroy()
+ ViewTreeLifecycleOwner.set(root, null)
+ ViewTreeSavedStateRegistryOwner.set(root, null)
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/SysuiTestTag.kt b/packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/SysuiTestTag.kt
new file mode 100644
index 000000000000..9eb78e14ab4e
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/SysuiTestTag.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.compose.modifiers
+
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.testTagsAsResourceId
+
+/**
+ * Set a test tag on this node so that it is associated with [resId]. This node will then be
+ * accessible by integration tests using `sysuiResSelector(resId)`.
+ */
+@OptIn(ExperimentalComposeUiApi::class)
+fun Modifier.sysuiResTag(resId: String): Modifier {
+ return this.semantics { testTagsAsResourceId = true }.testTag("com.android.systemui:id/$resId")
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
index 23dacf9946f3..3eeadae5385f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
@@ -51,6 +51,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.R
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
@@ -110,7 +111,9 @@ private fun PeopleScreenWithConversations(
recentTiles: List<PeopleTileViewModel>,
onTileClicked: (PeopleTileViewModel) -> Unit,
) {
- Column {
+ Column(
+ Modifier.sysuiResTag("top_level_with_conversations"),
+ ) {
Column(
Modifier.fillMaxWidth().padding(PeopleSpacePadding),
horizontalAlignment = Alignment.CenterHorizontally,
@@ -132,7 +135,7 @@ private fun PeopleScreenWithConversations(
}
LazyColumn(
- Modifier.fillMaxWidth(),
+ Modifier.fillMaxWidth().sysuiResTag("scroll_view"),
contentPadding =
PaddingValues(
top = 16.dp,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 5c5ceefbd6fb..349f5c333116 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -73,6 +73,7 @@ import com.android.systemui.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel
@@ -180,9 +181,9 @@ fun FooterActions(
security?.let { SecurityButton(it, Modifier.weight(1f)) }
foregroundServices?.let { ForegroundServicesButton(it) }
- userSwitcher?.let { IconButton(it) }
- IconButton(viewModel.settings)
- viewModel.power?.let { IconButton(it) }
+ userSwitcher?.let { IconButton(it, Modifier.sysuiResTag("multi_user_switch")) }
+ IconButton(viewModel.settings, Modifier.sysuiResTag("settings_button_container"))
+ viewModel.power?.let { IconButton(it, Modifier.sysuiResTag("pm_lite")) }
}
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt
index 5bb37071b075..cd9fb886a4e6 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt
@@ -20,12 +20,15 @@ package com.android.systemui.shared.customization.data.content
import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
+import android.content.Intent
import android.database.ContentObserver
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.net.Uri
+import android.util.Log
import androidx.annotation.DrawableRes
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
+import java.net.URISyntaxException
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -169,6 +172,8 @@ interface CustomizationProviderClient {
* If `null`, the button should not be shown.
*/
val enablementActionComponentName: String? = null,
+ /** Optional [Intent] to use to start an activity to configure this affordance. */
+ val configureIntent: Intent? = null,
)
/** Models a selection of a quick affordance on a slot. */
@@ -337,6 +342,11 @@ class CustomizationProviderClientImpl(
Contract.LockScreenQuickAffordances.AffordanceTable.Columns
.ENABLEMENT_COMPONENT_NAME
)
+ val configureIntentColumnIndex =
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+ .CONFIGURE_INTENT
+ )
if (
idColumnIndex == -1 ||
nameColumnIndex == -1 ||
@@ -344,15 +354,17 @@ class CustomizationProviderClientImpl(
isEnabledColumnIndex == -1 ||
enablementInstructionsColumnIndex == -1 ||
enablementActionTextColumnIndex == -1 ||
- enablementComponentNameColumnIndex == -1
+ enablementComponentNameColumnIndex == -1 ||
+ configureIntentColumnIndex == -1
) {
return@buildList
}
while (cursor.moveToNext()) {
+ val affordanceId = cursor.getString(idColumnIndex)
add(
CustomizationProviderClient.Affordance(
- id = cursor.getString(idColumnIndex),
+ id = affordanceId,
name = cursor.getString(nameColumnIndex),
iconResourceId = cursor.getInt(iconColumnIndex),
isEnabled = cursor.getInt(isEnabledColumnIndex) == 1,
@@ -367,6 +379,10 @@ class CustomizationProviderClientImpl(
cursor.getString(enablementActionTextColumnIndex),
enablementActionComponentName =
cursor.getString(enablementComponentNameColumnIndex),
+ configureIntent =
+ cursor
+ .getString(configureIntentColumnIndex)
+ ?.toIntent(affordanceId = affordanceId),
)
)
}
@@ -504,7 +520,19 @@ class CustomizationProviderClientImpl(
.onStart { emit(Unit) }
}
+ private fun String.toIntent(
+ affordanceId: String,
+ ): Intent? {
+ return try {
+ Intent.parseUri(this, 0)
+ } catch (e: URISyntaxException) {
+ Log.w(TAG, "Cannot parse Uri into Intent for affordance with ID \"$affordanceId\"!")
+ null
+ }
+ }
+
companion object {
+ private const val TAG = "CustomizationProviderClient"
private const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
index 1e2e7d2595ac..7f1c78fc47ff 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -113,6 +113,11 @@ object CustomizationProviderContract {
* opens a destination where the user can re-enable the disabled affordance.
*/
const val ENABLEMENT_COMPONENT_NAME = "enablement_action_intent"
+ /**
+ * Byte array. Optional parcelled `Intent` to use to start an activity that can be
+ * used to configure the affordance.
+ */
+ const val CONFIGURE_INTENT = "configure_intent"
}
}
diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp
index 7709f210f22f..fb1c454de70d 100644
--- a/packages/SystemUI/plugin/Android.bp
+++ b/packages/SystemUI/plugin/Android.bp
@@ -29,6 +29,7 @@ java_library {
"src/**/*.java",
"src/**/*.kt",
"bcsmartspace/src/**/*.java",
+ "bcsmartspace/src/**/*.kt",
],
static_libs: [
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt
new file mode 100644
index 000000000000..509f022310d0
--- /dev/null
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.plugins
+
+// TODO(b/265360975): Evaluate this plugin approach.
+/** Plugin to provide BC smartspace configuration */
+interface BcSmartspaceConfigPlugin {
+ /** Gets default date/weather disabled status. */
+ val isDefaultDateWeatherDisabled: Boolean
+}
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 51f5baa8b45f..bc6e5ec6117c 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -94,6 +94,11 @@ public interface BcSmartspaceDataPlugin extends Plugin {
void registerDataProvider(BcSmartspaceDataPlugin plugin);
/**
+ * Sets {@link BcSmartspaceConfigPlugin}.
+ */
+ void registerConfigProvider(BcSmartspaceConfigPlugin configProvider);
+
+ /**
* Primary color for unprotected text
*/
void setPrimaryTextColor(int color);
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 030eaa660c9f..5fc919330e92 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -21,12 +21,44 @@
# TODO(b/264686688): Handle these cases with more targeted annotations.
-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
private com.android.keyguard.KeyguardUpdateMonitorCallback *;
+ private com.android.systemui.privacy.PrivacyConfig$Callback *;
private com.android.systemui.privacy.PrivacyItemController$Callback *;
private com.android.systemui.settings.UserTracker$Callback *;
private com.android.systemui.statusbar.phone.StatusBarWindowCallback *;
private com.android.systemui.util.service.Observer$Callback *;
private com.android.systemui.util.service.ObservableServiceConnection$Callback *;
}
+# Note that these rules are temporary companions to the above rules, required
+# for cases like Kotlin where fields with anonymous types use the anonymous type
+# rather than the supertype.
+-if class * extends com.android.keyguard.KeyguardUpdateMonitorCallback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ <1> *;
+}
+-if class * extends com.android.systemui.privacy.PrivacyConfig$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ <1> *;
+}
+-if class * extends com.android.systemui.privacy.PrivacyItemController$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ <1> *;
+}
+-if class * extends com.android.systemui.settings.UserTracker$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ <1> *;
+}
+-if class * extends com.android.systemui.statusbar.phone.StatusBarWindowCallback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ <1> *;
+}
+-if class * extends com.android.systemui.util.service.Observer$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ <1> *;
+}
+-if class * extends com.android.systemui.util.service.ObservableServiceConnection$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ <1> *;
+}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index 2b7bdc2dc4cb..c772c9649cc7 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -27,7 +27,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+ androidprv:layout_maxWidth="@dimen/biometric_auth_pattern_view_max_size"
android:layout_gravity="center_horizontal|bottom"
android:clipChildren="false"
android:clipToPadding="false">
diff --git a/packages/SystemUI/res-keyguard/values-sw540dp-port/dimens.xml b/packages/SystemUI/res-keyguard/values-sw540dp-port/dimens.xml
deleted file mode 100644
index a3c37e420f29..000000000000
--- a/packages/SystemUI/res-keyguard/values-sw540dp-port/dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/assets/res/any/dimens.xml
-**
-** Copyright 2013, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
- <!-- Height of the sliding KeyguardSecurityContainer
- (includes 2x keyguard_security_view_top_margin) -->
- <dimen name="keyguard_security_height">550dp</dimen>
-</resources>
diff --git a/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml
index 1dc61c501beb..b7a1bb46c317 100644
--- a/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml
@@ -17,10 +17,5 @@
*/
-->
<resources>
-
- <!-- Height of the sliding KeyguardSecurityContainer
- (includes 2x keyguard_security_view_top_margin) -->
- <dimen name="keyguard_security_height">470dp</dimen>
-
<dimen name="widget_big_font_size">100dp</dimen>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index c5ffdc0051da..6cc5b9d7b7e8 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -29,9 +29,6 @@
(includes 2x keyguard_security_view_top_margin) -->
<dimen name="keyguard_security_height">420dp</dimen>
- <!-- Max Height of the sliding KeyguardSecurityContainer
- (includes 2x keyguard_security_view_top_margin) -->
-
<!-- pin/password field max height -->
<dimen name="keyguard_password_height">80dp</dimen>
diff --git a/packages/SystemUI/res-product/values-de/strings.xml b/packages/SystemUI/res-product/values-de/strings.xml
index b8d1a7ce5fe8..c82c7390e52e 100644
--- a/packages/SystemUI/res-product/values-de/strings.xml
+++ b/packages/SystemUI/res-product/values-de/strings.xml
@@ -43,6 +43,6 @@
<string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Entsperre dein Smartphone für weitere Optionen"</string>
<string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Entsperre dein Tablet für weitere Optionen"</string>
<string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Entsperre dein Gerät für weitere Optionen"</string>
- <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Wird auf diesem Smartphone abgespielt"</string>
- <string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Wird auf diesem Tablet abgespielt"</string>
+ <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Wiedergabe läuft auf diesem Smartphone"</string>
+ <string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Wiedergabe läuft auf diesem Tablet"</string>
</resources>
diff --git a/packages/SystemUI/res-product/values-fr/strings.xml b/packages/SystemUI/res-product/values-fr/strings.xml
index a07c8d8908a8..75a08628522b 100644
--- a/packages/SystemUI/res-product/values-fr/strings.xml
+++ b/packages/SystemUI/res-product/values-fr/strings.xml
@@ -43,6 +43,6 @@
<string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Déverrouillez votre téléphone pour obtenir plus d\'options"</string>
<string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Déverrouillez votre tablette pour obtenir plus d\'options"</string>
<string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Déverrouillez votre appareil pour obtenir plus d\'options"</string>
- <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Lecture sur ce téléphone…"</string>
+ <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Lecture sur ce téléphone"</string>
<string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Lecture sur cette tablette…"</string>
</resources>
diff --git a/packages/SystemUI/res-product/values-ka/strings.xml b/packages/SystemUI/res-product/values-ka/strings.xml
index 1b496ccddfb2..3474a7f8f67e 100644
--- a/packages/SystemUI/res-product/values-ka/strings.xml
+++ b/packages/SystemUI/res-product/values-ka/strings.xml
@@ -43,6 +43,6 @@
<string name="global_action_lock_message" product="default" msgid="7092460751050168771">"მეტი ვარიანტის სანახავად განბლოკეთ თქვენი ტელეფონი"</string>
<string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"მეტი ვარიანტის სანახავად განბლოკეთ თქვენი ტაბლეტი"</string>
<string name="global_action_lock_message" product="device" msgid="3165224897120346096">"მეტი ვარიანტის სანახავად განბლოკეთ თქვენი მოწყობილობა"</string>
- <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"უკრავს ტელეფონზე"</string>
+ <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"უკრავს ამ ტელეფონზე"</string>
<string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"უკრავს ამ ტაბლეტზე"</string>
</resources>
diff --git a/packages/SystemUI/res-product/values-pt-rBR/strings.xml b/packages/SystemUI/res-product/values-pt-rBR/strings.xml
index b287fd90f440..fed58e681606 100644
--- a/packages/SystemUI/res-product/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-product/values-pt-rBR/strings.xml
@@ -43,6 +43,6 @@
<string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Desbloqueie seu smartphone para ver mais opções"</string>
<string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Desbloqueie seu tablet para ver mais opções"</string>
<string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Desbloqueie seu dispositivo para ver mais opções"</string>
- <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Mídia aberta neste smartphone"</string>
- <string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Mídia aberta neste tablet"</string>
+ <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Mídia tocando neste smartphone"</string>
+ <string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Mídia tocando neste tablet"</string>
</resources>
diff --git a/packages/SystemUI/res-product/values-pt/strings.xml b/packages/SystemUI/res-product/values-pt/strings.xml
index b287fd90f440..fed58e681606 100644
--- a/packages/SystemUI/res-product/values-pt/strings.xml
+++ b/packages/SystemUI/res-product/values-pt/strings.xml
@@ -43,6 +43,6 @@
<string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Desbloqueie seu smartphone para ver mais opções"</string>
<string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Desbloqueie seu tablet para ver mais opções"</string>
<string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Desbloqueie seu dispositivo para ver mais opções"</string>
- <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Mídia aberta neste smartphone"</string>
- <string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Mídia aberta neste tablet"</string>
+ <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Mídia tocando neste smartphone"</string>
+ <string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Mídia tocando neste tablet"</string>
</resources>
diff --git a/packages/SystemUI/res-product/values-uz/strings.xml b/packages/SystemUI/res-product/values-uz/strings.xml
index bf8d94a63351..58309ff3471d 100644
--- a/packages/SystemUI/res-product/values-uz/strings.xml
+++ b/packages/SystemUI/res-product/values-uz/strings.xml
@@ -43,6 +43,6 @@
<string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Boshqa parametrlar uchun telefoningiz qulfini oching"</string>
<string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Boshqa parametrlar uchun planshetingiz qulfini oching"</string>
<string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Boshqa parametrlar uchun qurilmangiz qulfini oching"</string>
- <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Bu telefonda ijro etilmoqda"</string>
+ <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Bu telefonda ijro qilinmoqda"</string>
<string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Bu planshetda ijro etilmoqda"</string>
</resources>
diff --git a/packages/SystemUI/res/drawable/ic_media_explicit_indicator.xml b/packages/SystemUI/res/drawable/ic_media_explicit_indicator.xml
new file mode 100644
index 000000000000..08c5aaf56bf7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_media_explicit_indicator.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="13dp"
+ android:height="13dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18.3,34H29.65V31H21.3V25.7H29.65V22.7H21.3V17.35H29.65V14.35H18.3ZM9,42Q7.8,42 6.9,41.1Q6,40.2 6,39V9Q6,7.8 6.9,6.9Q7.8,6 9,6H39Q40.2,6 41.1,6.9Q42,7.8 42,9V39Q42,40.2 41.1,41.1Q40.2,42 39,42ZM9,39H39Q39,39 39,39Q39,39 39,39V9Q39,9 39,9Q39,9 39,9H9Q9,9 9,9Q9,9 9,9V39Q9,39 9,39Q9,39 9,39ZM9,9Q9,9 9,9Q9,9 9,9V39Q9,39 9,39Q9,39 9,39Q9,39 9,39Q9,39 9,39V9Q9,9 9,9Q9,9 9,9Z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
index a3dd334bd667..3505a3e6b6bf 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
@@ -71,8 +71,8 @@
<com.android.internal.widget.LockPatternView
android:id="@+id/lockPattern"
android:layout_gravity="center"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
+ android:layout_width="@dimen/biometric_auth_pattern_view_size"
+ android:layout_height="@dimen/biometric_auth_pattern_view_size"/>
<TextView
android:id="@+id/error"
diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
index 4af997017bba..147ea8221beb 100644
--- a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
@@ -67,8 +67,8 @@
<com.android.internal.widget.LockPatternView
android:id="@+id/lockPattern"
android:layout_gravity="center"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
+ android:layout_width="@dimen/biometric_auth_pattern_view_size"
+ android:layout_height="@dimen/biometric_auth_pattern_view_size"/>
<TextView
android:id="@+id/error"
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index 95aefab328df..abc83379950a 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -147,6 +147,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
+ <!-- Explicit Indicator -->
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/media_explicit_indicator"
+ android:layout_width="@dimen/qs_media_explicit_indicator_icon_size"
+ android:layout_height="@dimen/qs_media_explicit_indicator_icon_size"
+ android:src="@drawable/ic_media_explicit_indicator"
+ />
+
<!-- Artist name -->
<TextView
android:id="@+id/header_artist"
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
index 5aa608084510..d1a2cf4c24b2 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -25,6 +25,7 @@
android:focusable="true"
android:clipChildren="false"
android:clipToPadding="false"
+ android:paddingStart="8dp"
>
<LinearLayout
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index c0509a9f7702..fbe6280f71bd 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Ondergrens <xliff:g id="PERCENT">%1$d</xliff:g> persent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Linkergrens <xliff:g id="PERCENT">%1$d</xliff:g> persent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Regtergrens <xliff:g id="PERCENT">%1$d</xliff:g> persent"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Werkskermskote word in die <xliff:g id="APP">%1$s</xliff:g>-app gestoor"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Lêers"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skermopnemer"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Verwerk tans skermopname"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Deurlopende kennisgewing vir \'n skermopnamesessie"</string>
@@ -373,11 +375,11 @@
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Begin opneem of uitsaai met <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"Laat <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> toe om te deel of op te neem?"</string>
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"Hele skerm"</string>
- <string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"’n Enkele program"</string>
+ <string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"’n Enkele app"</string>
<string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"Wanneer jy deel, opneem of uitsaai, het <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> toegang tot enigiets wat op jou skerm sigbaar is of op jou toestel gespeel word. Wees dus versigtig met wagwoorde, betalingbesonderhede, boodskappe of ander sensitiewe inligting."</string>
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Wanneer jy ’n program deel, opneem of uitsaai, het <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> toegang tot enigiets wat in daardie program sigbaar is of daarin gespeel word. Wees dus versigtig met wagwoorde, betalingbesonderhede, boodskappe of ander sensitiewe inligting."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Gaan voort"</string>
- <string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Deel of neem ’n program op"</string>
+ <string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Deel of neem ’n app op"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"Laat hierdie app toe om te deel of op te neem?"</string>
<string name="media_projection_permission_dialog_system_service_warning_entire_screen" msgid="8801616203805837575">"Wanneer jy deel, opneem of uitsaai, het hierdie app toegang tot enigiets wat op jou skerm sigbaar is of op jou toestel gespeel word. Wees dus versigtig met wagwoorde, betalingbesonderhede, boodskappe of ander sensitiewe inligting."</string>
<string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"Wanneer jy ’n app deel, opneem of uitsaai, het hierdie app toegang tot enigiets wat in daardie program sigbaar is of daarin gespeel word. Wees dus versigtig met wagwoorde, betalingbesonderhede, boodskappe of ander sensitiewe inligting."</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Speel <xliff:g id="SONG_NAME">%1$s</xliff:g> vanaf <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Ontdoen"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Beweeg nader om op <xliff:g id="DEVICENAME">%1$s</xliff:g> te speel"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Beweeg nader aan <xliff:g id="DEVICENAME">%1$s</xliff:g> om hier te speel"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Speel tans op <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Iets is fout. Probeer weer."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Laai tans"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Onaktief, gaan program na"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nie gekry nie"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrole is nie beskikbaar nie"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Luidsprekers en skerms"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Voorgestelde toestelle"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitsaai werk"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Saai uit"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Mense in jou omtrek met versoenbare Bluetooth-toestelle kan na die media luister wat jy uitsaai"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Hierdie skerm sal afskakel"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Voubare toestel word ontvou"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Voubare toestel word omgekeer"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Stilus se battery is amper pap"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index d0c5c9040ea8..0a26c1d4354c 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"የታች ወሰን <xliff:g id="PERCENT">%1$d</xliff:g> በመቶ"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"የግራ ወሰን <xliff:g id="PERCENT">%1$d</xliff:g> በመቶ"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"የቀኝ ወሰን <xliff:g id="PERCENT">%1$d</xliff:g> በመቶ"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"የማያ መቅጃ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"የማያ ገጽ ቀረጻን በማሰናዳት ላይ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ለአንድ የማያ ገጽ ቀረጻ ክፍለ-ጊዜ በመካሄድ ያለ ማሳወቂያ"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ከ<xliff:g id="APP_LABEL">%2$s</xliff:g> ያጫውቱ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ቀልብስ"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"በ<xliff:g id="DEVICENAME">%1$s</xliff:g> ላይ ለማጫወት ጠጋ ያድርጉ"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"እዚህ ለመጫወት ወደ <xliff:g id="DEVICENAME">%1$s</xliff:g> ቀረብ ይበሉ"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"በ<xliff:g id="DEVICENAME">%1$s</xliff:g> ላይ በማጫወት ላይ"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"የሆነ ችግር ተፈጥሯል። እንደገና ይሞክሩ።"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"በመጫን ላይ"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"ንቁ ያልኾነ፣ መተግበሪያን ይፈትሹ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"አልተገኘም"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"የድምጽ መጠን"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ድምጽ ማውጫዎች እና ማሳያዎች"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ማሰራጨት እንዴት እንደሚሠራ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ስርጭት"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ተኳሃኝ የብሉቱዝ መሣሪያዎች ያላቸው በአቅራቢያዎ ያሉ ሰዎች እርስዎ እያሰራጩት ያሉትን ሚዲያ ማዳመጥ ይችላሉ"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ይህ ማያ ገጽ ይጠፋል"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"መታጠፍ የሚችል መሣሪያ እየተዘረጋ ነው"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"መታጠፍ የሚችል መሣሪያ እየተገለበጠ ነው"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"የብሮስፌ ባትሪ ዝቅተኛ ነው"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index ea5bce12b3db..d641887c6153 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"الحد السفلى <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"الحد الأيسر <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"الحد الأيمن <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"مسجّل الشاشة"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"جارٍ معالجة تسجيل الشاشة"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"إشعار مستمر لجلسة تسجيل شاشة"</string>
@@ -371,7 +375,7 @@
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"ستتمكن الخدمة التي تقدّم هذه الوظيفة من الوصول إلى كل المعلومات المرئية لك على الشاشة أو التي يتم تشغيلها على جهازك أثناء التسجيل أو البث. ويشمل ذلك معلومات مثل كلمات المرور وتفاصيل الدفع والصور والرسائل والمقاطع الصوتية التي تشغِّلها."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"هل تريد بدء التسجيل أو البث؟"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"هل تريد بدء التسجيل أو الإرسال باستخدام <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>؟"</string>
- <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"هل تريد السماح لتطبيق <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>بالمشاركة أو التسجيل؟"</string>
+ <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"هل تريد السماح لتطبيق <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> بالمشاركة أو التسجيل؟"</string>
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"الشاشة بالكامل"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"تطبيق واحد"</string>
<string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"أثناء المشاركة أو التسجيل أو البث، يمكن لتطبيق <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> الوصول إلى كل العناصر المرئية على شاشتك أو التي يتم تشغيلها على جهازك، لذا يُرجى توخي الحذر بشأن كلمات المرور أو تفاصيل الدفع أو الرسائل أو المعلومات الحساسة الأخرى."</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"تشغيل <xliff:g id="SONG_NAME">%1$s</xliff:g> من تطبيق <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"تراجع"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"عليك الاقتراب لتشغيل الوسائط على <xliff:g id="DEVICENAME">%1$s</xliff:g>."</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"يُرجى الاقتراب من <xliff:g id="DEVICENAME">%1$s</xliff:g> لتشغيل الوسائط هنا."</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"جارٍ تشغيل الوسائط على <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"حدث خطأ. يُرجى إعادة المحاولة."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"جارٍ التحميل"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"غير نشط، تحقّق من التطبيق."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"لم يتم العثور عليه."</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"مستوى الصوت"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"مكبّرات الصوت والشاشات"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"كيفية عمل البث"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"البث"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"يمكن للأشخاص القريبين منك الذين لديهم أجهزة متوافقة تتضمّن بلوتوث الاستماع إلى الوسائط التي تبثها."</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"* سيتم إطفاء هذه الشاشة."</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"جهاز قابل للطي يجري فتحه"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"جهاز قابل للطي يجري قلبه"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"بطارية قلم الشاشة منخفضة"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index cbd2bedd3f20..e376a6c14bbb 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"তলৰ সীমা <xliff:g id="PERCENT">%1$d</xliff:g> শতাংশ"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"বাওঁফালৰ সীমা <xliff:g id="PERCENT">%1$d</xliff:g> শতাংশ"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"সোঁফালৰ সীমা <xliff:g id="PERCENT">%1$d</xliff:g> শতাংশ"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"স্ক্ৰীন ৰেকৰ্ডাৰ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"স্ক্রীন ৰেকৰ্ডিঙৰ প্ৰক্ৰিয়াকৰণ হৈ আছে"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রীন ৰেকৰ্ডিং ছেশ্বন চলি থকা সময়ত পোৱা জাননী"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>ত <xliff:g id="SONG_NAME">%1$s</xliff:g> গীতটো প্লে’ কৰক"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"আনডু কৰক"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ত প্লে’ কৰিবলৈ ওচৰলৈ যাওক"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"ইয়াত খেলিবলৈ <xliff:g id="DEVICENAME">%1$s</xliff:g>ৰ আৰু ওচৰলৈ যাওক"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ত প্লে কৰি থকা হৈছে"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"কিবা ভুল হ’ল। পুনৰ চেষ্টা কৰক।"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"ল’ড হৈ আছে"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"সক্ৰিয় নহয়, এপ্‌টো পৰীক্ষা কৰক"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"বিচাৰি পোৱা নগ’ল"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ভলিউম"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"স্পীকাৰ আৰু ডিছপ্লে’"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"সম্প্ৰচাৰ কৰাটোৱে কেনেকৈ কাম কৰে"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"সম্প্ৰচাৰ"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"সমিল ব্লুটুথ ডিভাইচৰ সৈতে আপোনাৰ নিকটৱৰ্তী স্থানত থকা লোকসকলে আপুনি সম্প্ৰচাৰ কৰা মিডিয়াটো শুনিব পাৰে"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ই স্ক্ৰীনখন অফ হ’ব"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"জপাব পৰা ডিভাইচৰ জাপ খুলি থকা হৈছে"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"জপাব পৰা ডিভাইচৰ ওলোটাই থকা হৈছে"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"ষ্টাইলাছৰ বেটাৰী কম আছে"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index dd4d5109855d..a2f20cea73fc 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Aşağı sərhəd <xliff:g id="PERCENT">%1$d</xliff:g> faiz"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Sol sərhəd <xliff:g id="PERCENT">%1$d</xliff:g> faiz"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Sağ sərhəd <xliff:g id="PERCENT">%1$d</xliff:g> faiz"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Ekran Yazıcısı"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran çəkilişi emal edilir"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekranın video çəkimi ərzində silinməyən bildiriş"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> mahnısını <xliff:g id="APP_LABEL">%2$s</xliff:g> tətbiqindən oxudun"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Geri qaytarın"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında oxutmaq üçün yaxınlaşın"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Burada oxutmaq üçün <xliff:g id="DEVICENAME">%1$s</xliff:g> cihazına yaxınlaşın"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında oxudulur"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Xəta oldu. Yenə cəhd edin."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Yüklənir"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Aktiv deyil, tətbiqi yoxlayın"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Tapılmadı"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Səs"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Dinamiklər &amp; Displeylər"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Yayım necə işləyir"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Yayım"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Uyğun Bluetooth cihazları olan yaxınlığınızdakı insanlar yayımladığınız medianı dinləyə bilər"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Bu ekran deaktiv ediləcək"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Qatlana bilən cihaz açılır"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Qatlana bilən cihaz fırladılır"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Qələm enerjisi azdır"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 64313896a4ce..19209870ac4d 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Donja ivica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Leva ivica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Desna ivica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrađujemo video snimka ekrana"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Obaveštenje o sesiji snimanja ekrana je aktivno"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Opozovi"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Približite da biste puštali muziku na: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Približite se uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g> da biste na njemu puštali"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Pušta se na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Došlo je do greške. Probajte ponovo."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Učitava se"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno. Vidite aplikaciju"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Zvuk"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvučnici i ekrani"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako funkcioniše emitovanje"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Emitovanje"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Ljudi u blizini sa kompatibilnim Bluetooth uređajima mogu da slušaju medijski sadržaj koji emitujete"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ovaj ekran će se isključiti"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Uređaj na preklop se otvara"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Uređaj na preklop se obrće"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Nizak nivo baterije pisaljke"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 7e7262a057d3..537622e07e2a 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Ніжняя граніца: <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Левая граніца: <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Правая граніца: <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Запіс экрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Апрацоўваецца запіс экрана"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Бягучае апавяшчэнне для сеанса запісу экрана"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Прайграйце кампазіцыю \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" з дапамогай праграмы \"<xliff:g id="APP_LABEL">%2$s</xliff:g>\""</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Адрабіць"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Каб прайграць мультымедыя на прыладзе \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\", наблізьцеся да яе"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Падыдзіце бліжэй да прылады \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\", каб прайграць на гэтай"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Прайграецца на прыладзе \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\""</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Нешта пайшло не так. Паўтарыце спробу."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Ідзе загрузка"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Неактыўна, праверце праграму"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не знойдзена"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Гучнасць"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Дынамікі і дысплэі"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Як адбываецца трансляцыя"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Трансляцыя"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Людзі паблізу, у якіх ёсць прылады з Bluetooth, змогуць праслухваць мультымедыйнае змесціва, якое вы трансліруеце"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Гэты экран будзе выключаны"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Складная прылада ў раскладзеным выглядзе"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Перавернутая складная прылада"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Нізкі ўзровень зараду пяра"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 26a154fadc56..eba0d0333033 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Долна граница: <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Лява граница: <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Дясна граница: <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Запис на екрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Записът на екрана се обработва"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущо известие за сесия за записване на екрана"</string>
@@ -375,12 +379,12 @@
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"Цял екран"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"Едно приложение"</string>
<string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"Когато споделяте, записвате или предавате, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има достъп до всичко, което се вижда на екрана ви или се възпроизвежда на устройството ви, затова бъдете внимателни с пароли, подробности за начини на плащане, съобщения или друга поверителна информация."</string>
- <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Когато споделяте, записвате или предавате, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има достъп до всичко, което се показва или възпроизвежда в това приложение, затова бъдете внимателни с пароли, подробности за начини на плащане, съобщения или друга поверителна информация."</string>
+ <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Когато споделяте, записвате или предавате приложение, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има достъп до всичко, което се показва или възпроизвежда в това приложение, затова бъдете внимателни с пароли, подробности за начини на плащане, съобщения или друга поверителна информация."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Напред"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Споделяне или записване на приложение"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"Да се разреши ли на това приложение да споделя или записва?"</string>
<string name="media_projection_permission_dialog_system_service_warning_entire_screen" msgid="8801616203805837575">"Когато споделяте, записвате или предавате, това приложение има достъп до всичко, което се вижда на екрана ви или се възпроизвежда на устройството ви, затова бъдете внимателни с пароли, подробности за начини на плащане, съобщения или друга поверителна информация."</string>
- <string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"Когато споделяте, записвате или предавате, това приложение има достъп до всичко, което се показва или възпроизвежда в него, затова бъдете внимателни с пароли, подробности за начини на плащане, съобщения или друга поверителна информация."</string>
+ <string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"Когато споделяте, записвате или предавате приложение, това приложение има достъп до всичко, което се показва или възпроизвежда в приложението, затова бъдете внимателни с пароли, подробности за начини на плащане, съобщения или друга поверителна информация."</string>
<string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"Блокирано от системния ви администратор"</string>
<string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"Заснемането на екрана е деактивирано от правило за устройството"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Изчистване на всички"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Пускане на <xliff:g id="SONG_NAME">%1$s</xliff:g> от <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Отмяна"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Преместете се по-близо, за да се възпроизведе на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Приближете се до <xliff:g id="DEVICENAME">%1$s</xliff:g> за възпроизвеждане на това устройство"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Възпроизвежда се на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Нещо се обърка. Опитайте отново."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Зарежда се"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивно, проверете прилож."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не е намерено"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Сила на звука"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Високоговорители и екрани"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Как работи предаването"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Предаване"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Хората в близост със съвместими устройства с Bluetooth могат да слушат мултимедията, която предавате"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Този екран ще се изключи"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Разгъване на сгъваемо устройство"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Обръщане на сгъваемо устройство"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Батерията на писалката е изтощена"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index b6069a4a9361..13353aa1a0be 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"নিচের প্রান্ত থেকে <xliff:g id="PERCENT">%1$d</xliff:g> শতাংশ"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"বাঁ প্রান্ত থেকে <xliff:g id="PERCENT">%1$d</xliff:g> শতাংশ"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"ডান প্রান্ত থেকে <xliff:g id="PERCENT">%1$d</xliff:g> percent"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"অফিসের স্ক্রিনশট <xliff:g id="APP">%1$s</xliff:g> অ্যাপে সেভ করা হয়"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ফাইল"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"স্ক্রিন রেকর্ডার"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"স্ক্রিন রেকর্ডিং প্রসেস হচ্ছে"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রিন রেকর্ডিং সেশন চলার বিজ্ঞপ্তি"</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> গানটি <xliff:g id="APP_LABEL">%2$s</xliff:g> অ্যাপে চালান"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"আগের অবস্থায় ফিরুন"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>-এ চালাতে আরও কাছে আনুন"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"এখান থেকে চালাতে <xliff:g id="DEVICENAME">%1$s</xliff:g>-এর কাছে নিয়ে যান"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g>-এ ভিডিও চালানো হচ্ছে"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"কোনও সমস্যা হয়েছে। আবার চেষ্টা করুন।"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"লোড করা হচ্ছে"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ট্যাবলেট"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"বন্ধ আছে, অ্যাপ চেক করুন"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"খুঁজে পাওয়া যায়নি"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"কন্ট্রোল উপলভ্য নেই"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ভলিউম"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"স্পিকার &amp; ডিসপ্লে"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"সাজেস্ট করা ডিভাইস"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ব্রডকাস্ট কীভাবে কাজ করে"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"সম্প্রচার করুন"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"আশপাশে লোকজন যাদের মানানসই ব্লুটুথ ডিভাইস আছে, তারা আপনার ব্রডকাস্ট করা মিডিয়া শুনতে পারবেন"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ এই স্ক্রিন বন্ধ হয়ে যাবে"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ফোল্ড করা যায় এমন ডিভাইস খোলা হচ্ছে"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ফোল্ড করা যায় এমন ডিভাইস উল্টানো হচ্ছে"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"স্টাইলাস ব্যাটারিতে চার্জ কম আছে"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 67efa7570e4f..2475a477c15d 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Donja granica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Lijeva granica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Desna granica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Poslovni snimci ekrana se pohranjuju u aplikaciji <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fajlovi"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrađivanje snimka ekrana"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Obavještenje za sesiju snimanja ekrana je u toku"</string>
@@ -849,12 +851,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reproducirajte pjesmu <xliff:g id="SONG_NAME">%1$s</xliff:g> izvođača <xliff:g id="ARTIST_NAME">%2$s</xliff:g> pomoću aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproducirajte pjesmu <xliff:g id="SONG_NAME">%1$s</xliff:g> pomoću aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Poništi"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Približite se da reproducirate na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Približite se uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g> da na njemu reproducirate"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Približite da reproducirate na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Reproducira se na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Nešto nije uredu. Pokušajte ponovo."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Učitavanje"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, vidite aplikaciju"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrola nije dostupna"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Jačina zvuka"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvučnici i ekrani"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Predloženi uređaji"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako funkcionira emitiranje"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Emitirajte"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osobe u vašoj blizini s kompatibilnim Bluetooth uređajima mogu slušati medijske sadržaje koje emitirate"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ekran će se isključiti"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Sklopivi uređaj se rasklapa"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Sklopivi uređaj se obrće"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Baterija pisaljke je slaba"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 2f48676a142a..ef732184a9f6 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Marge inferior <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Marge esquerre <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Marge dret <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Gravació de pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processant gravació de pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificació en curs d\'una sessió de gravació de la pantalla"</string>
@@ -98,7 +102,7 @@
<string name="screenrecord_description" msgid="1123231719680353736">"Durant la gravació, el sistema Android pot capturar qualsevol informació sensible que es mostri a la pantalla o que es reprodueixi al dispositiu. Això inclou contrasenyes, informació de pagament, fotos, missatges i àudio."</string>
<string name="screenrecord_option_entire_screen" msgid="1732437834603426934">"Grava la pantalla completa"</string>
<string name="screenrecord_option_single_app" msgid="5954863081500035825">"Grava una sola aplicació"</string>
- <string name="screenrecord_warning_entire_screen" msgid="8141407178104195610">"Mentre graves, Android té accés a qualsevol cosa que es vegi a la pantalla o que es reprodueixi al dispositiu. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges o altra informació sensible."</string>
+ <string name="screenrecord_warning_entire_screen" msgid="8141407178104195610">"Mentre graves contingut, Android pot accedir a tot el que es veu a la pantalla o que es reprodueix al dispositiu. Per això cal que vagis amb compte amb les contrasenyes, les dades de pagament, els missatges o qualsevol altra informació sensible."</string>
<string name="screenrecord_warning_single_app" msgid="7760723997065948283">"Mentre graves una aplicació, Android té accés a qualsevol cosa que es vegi a la pantalla o que es reprodueixi a l\'aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges o altra informació sensible."</string>
<string name="screenrecord_start_recording" msgid="348286842544768740">"Inicia la gravació"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Grava l\'àudio"</string>
@@ -371,11 +375,11 @@
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"El servei que ofereix aquesta funció tindrà accés a tota la informació visible a la teva pantalla o que es reprodueix al dispositiu mentre graves o emets contingut, com ara contrasenyes, detalls dels pagaments, fotos, missatges i àudio que reprodueixis."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Vols començar a gravar o emetre contingut?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Vols començar a gravar o emetre contingut amb <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"Vols permetre que <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> comparteixi o gravi?"</string>
+ <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"Vols permetre que <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> comparteixi o gravi contingut?"</string>
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"Tota la pantalla"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"Una sola aplicació"</string>
<string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"Quan estàs compartint, gravant o emetent, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> té accés a qualsevol cosa que es vegi a la pantalla o que es reprodueixi al dispositiu. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges o altra informació sensible."</string>
- <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Quan estàs compartint, gravant o emetent, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> té accés a qualsevol cosa que es vegi a la pantalla o que es reprodueixi a l\'aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges o altra informació sensible."</string>
+ <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Mentre comparteixes, graves o emets contingut, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> pot accedir a tot el que es veu a la pantalla o que es reprodueix a l\'aplicació. Per això cal que vagis amb compte amb les contrasenyes, les dades de pagament, els missatges o qualsevol altra informació sensible."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continua"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Comparteix o grava una aplicació"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"Vols permetre que aquesta aplicació comparteixi o gravi contingut?"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reprodueix <xliff:g id="SONG_NAME">%1$s</xliff:g> des de l\'aplicació <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desfés"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Mou més a prop per reproduir a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Acosta\'t a <xliff:g id="DEVICENAME">%1$s</xliff:g> per reproduir el contingut aquí"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"S\'està reproduint a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"S\'ha produït un error. Torna-ho a provar."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"S\'està carregant"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Inactiu; comprova l\'aplicació"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"No s\'ha trobat"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volum"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altaveus i pantalles"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Com funciona l\'emissió"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Emet"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les persones properes amb dispositius Bluetooth compatibles poden escoltar el contingut multimèdia que emets"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Aquesta pantalla s\'apagarà"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositiu plegable desplegant-se"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositiu plegable girant"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria del llapis òptic baixa"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index feeaa32f68a7..3c8c6da4ff5c 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Dolní okraj <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Levý okraj <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Pravý okraj <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Záznam obrazovky se zpracovává"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Trvalé oznámení o relaci nahrávání"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Přehrát skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> z aplikace <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Vrátit zpět"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Pokud chcete přehrávat na zařízení <xliff:g id="DEVICENAME">%1$s</xliff:g>, přibližte se k němu"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Pokud zde chcete přehrávat média, přibližte se k zařízení <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Přehrávání v zařízení <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Došlo k chybě. Zkuste to znovu."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Načítání"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivní, zkontrolujte aplikaci"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nenalezeno"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Hlasitost"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Reproduktory a displeje"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jak vysílání funguje"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Vysílání"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Lidé ve vašem okolí s kompatibilními zařízeními Bluetooth mohou poslouchat média, která vysíláte"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Tato obrazovka se vypne"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Rozkládání rozkládacího zařízení"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Otáčení rozkládacího zařízení"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Slabá baterie dotykového pera"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index f64587e26731..585d85838e81 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Nederste kant: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Venstre kant: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Højre kant: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Skærmoptagelse"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skærmoptagelse"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Konstant notifikation om skærmoptagelse"</string>
@@ -849,11 +853,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Afspil <xliff:g id="SONG_NAME">%1$s</xliff:g> af <xliff:g id="ARTIST_NAME">%2$s</xliff:g> via <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Afspil <xliff:g id="SONG_NAME">%1$s</xliff:g> via <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Fortryd"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Flyt enheden tættere på for at afspille på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Ryk tættere på <xliff:g id="DEVICENAME">%1$s</xliff:g> for at afspille her"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Ryk tættere på for at afspille på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Afspilles på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Noget gik galt. Prøv igen."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Indlæser"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Tjek appen"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ikke fundet"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Lydstyrke"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Højttalere og skærme"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Sådan fungerer udsendelser"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Udsendelse"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personer i nærheden, som har kompatible Bluetooth-enheder, kan lytte til det medie, du udsender"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ *Denne skærm slukkes"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldbar enhed foldes ud"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldbar enhed vendes om"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Lavt batteriniveau på styluspen"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index bd4c5872dfce..078c02d2b259 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Unterer Rand <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Linker Rand <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Rechter Rand <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Bildschirmaufzeichnung"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Bildschirmaufzeichnung…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Fortlaufende Benachrichtigung für eine Bildschirmaufzeichnung"</string>
@@ -98,8 +102,8 @@
<string name="screenrecord_description" msgid="1123231719680353736">"Beim Aufnehmen kann das Android-System vertrauliche Informationen erfassen, die auf deinem Bildschirm angezeigt oder von deinem Gerät wiedergegeben werden. Das können Passwörter, Zahlungsinformationen, Fotos, Nachrichten und Audioinhalte sein."</string>
<string name="screenrecord_option_entire_screen" msgid="1732437834603426934">"Gesamten Bildschirm aufnehmen"</string>
<string name="screenrecord_option_single_app" msgid="5954863081500035825">"Eine einzelne App aufnehmen"</string>
- <string name="screenrecord_warning_entire_screen" msgid="8141407178104195610">"Während der Aufnahme hat Android Zugriff auf alle Inhalte, die auf dem Bildschirm sichtbar sind oder auf dem Gerät wiedergegeben werden. Sei daher mit Passwörtern, Zahlungsdetails, Nachrichten oder anderen vertraulichen Informationen vorsichtig."</string>
- <string name="screenrecord_warning_single_app" msgid="7760723997065948283">"Während der Aufnahme einer App hat Android Zugriff auf alle Inhalte, die in dieser App sichtbar sind oder wiedergegeben werden. Sei daher mit Passwörtern, Zahlungsdetails, Nachrichten oder anderen vertraulichen Informationen vorsichtig."</string>
+ <string name="screenrecord_warning_entire_screen" msgid="8141407178104195610">"Während der Aufnahme hat Android Zugriff auf alle Inhalte, die auf dem Bildschirm sichtbar sind oder auf dem Gerät wiedergegeben werden. Sei daher vorsichtig mit Passwörtern, Zahlungsdetails, Nachrichten oder anderen vertraulichen Informationen."</string>
+ <string name="screenrecord_warning_single_app" msgid="7760723997065948283">"Während der Aufnahme einer App hat Android Zugriff auf alle Inhalte, die in dieser App sichtbar sind oder wiedergegeben werden. Sei daher vorsichtig mit Passwörtern, Zahlungsdetails, Nachrichten oder anderen vertraulichen Informationen."</string>
<string name="screenrecord_start_recording" msgid="348286842544768740">"Aufnahme starten"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Audio aufnehmen"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio des Geräts"</string>
@@ -374,13 +378,13 @@
<string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"Zulassen, dass <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> Inhalte teilt oder aufnimmt?"</string>
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"Gesamter Bildschirm"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"Eine einzelne App"</string>
- <string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"Beim Teilen, Aufnehmen oder Übertragen hat <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> Zugriff auf alle Inhalte, die auf dem Bildschirm sichtbar sind oder auf dem Gerät wiedergegeben werden. Sei daher mit Passwörtern, Zahlungsdetails, Nachrichten oder anderen vertraulichen Informationen vorsichtig."</string>
- <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Beim Teilen, Aufnehmen oder Übertragen einer App hat <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> Zugriff auf alle Inhalte, die in dieser App sichtbar sind oder wiedergegeben werden. Sei daher mit Passwörtern, Zahlungsdetails, Nachrichten oder anderen vertraulichen Informationen vorsichtig."</string>
+ <string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"Beim Teilen, Aufnehmen oder Übertragen hat <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> Zugriff auf alle Inhalte, die auf dem Bildschirm sichtbar sind oder auf dem Gerät wiedergegeben werden. Sei daher vorsichtig mit Passwörtern, Zahlungsdetails, Nachrichten oder anderen vertraulichen Informationen."</string>
+ <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Beim Teilen, Aufnehmen oder Übertragen einer App hat <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> Zugriff auf alle Inhalte, die in dieser App sichtbar sind oder wiedergegeben werden. Sei daher vorsichtig mit Passwörtern, Zahlungsdetails, Nachrichten oder anderen vertraulichen Informationen."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Weiter"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"App teilen oder aufnehmen"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"Dieser App das Teilen oder Aufnehmen erlauben?"</string>
- <string name="media_projection_permission_dialog_system_service_warning_entire_screen" msgid="8801616203805837575">"Beim Teilen, Aufnehmen oder Übertragen hat diese App Zugriff auf alle Inhalte, die auf dem Bildschirm sichtbar sind oder auf dem Gerät wiedergegeben werden. Sei daher mit Passwörtern, Zahlungsdetails, Nachrichten oder anderen vertraulichen Informationen vorsichtig."</string>
- <string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"Beim Teilen, Aufnehmen oder Übertragen einer App hat diese App Zugriff auf alle Inhalte, die in dieser App sichtbar sind oder wiedergegeben werden. Sei daher mit Passwörtern, Zahlungsdetails, Nachrichten oder anderen vertraulichen Informationen vorsichtig."</string>
+ <string name="media_projection_permission_dialog_system_service_warning_entire_screen" msgid="8801616203805837575">"Beim Teilen, Aufnehmen oder Übertragen hat diese App Zugriff auf alle Inhalte, die auf dem Bildschirm sichtbar sind oder auf dem Gerät wiedergegeben werden. Sei daher vorsichtig mit Passwörtern, Zahlungsdetails, Nachrichten oder anderen vertraulichen Informationen."</string>
+ <string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"Beim Teilen, Aufnehmen oder Übertragen einer App hat diese App Zugriff auf alle Inhalte, die in dieser App sichtbar sind oder wiedergegeben werden. Sei daher vorsichtig mit Passwörtern, Zahlungsdetails, Nachrichten oder anderen vertraulichen Informationen."</string>
<string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"Vom IT-Administrator blockiert"</string>
<string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"Bildschirmaufnahme ist durch die Geräterichtlinien deaktiviert"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Alle löschen"</string>
@@ -849,11 +853,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> von <xliff:g id="ARTIST_NAME">%2$s</xliff:g> über <xliff:g id="APP_LABEL">%3$s</xliff:g> wiedergeben"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> über <xliff:g id="APP_LABEL">%2$s</xliff:g> wiedergeben"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Rückgängig machen"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Gehe für die Wiedergabe näher an <xliff:g id="DEVICENAME">%1$s</xliff:g> heran"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Platziere für die Wiedergabe dein Gerät näher an „<xliff:g id="DEVICENAME">%1$s</xliff:g>“"</string>
- <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Wird auf „<xliff:g id="DEVICENAME">%1$s</xliff:g>“ abgespielt"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Gehe für die Wiedergabe näher an „<xliff:g id="DEVICENAME">%1$s</xliff:g>“ heran"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
+ <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Wiedergabe läuft auf „<xliff:g id="DEVICENAME">%1$s</xliff:g>“"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Es gab ein Problem. Versuch es noch einmal."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Wird geladen"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv – sieh in der App nach"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nicht gefunden"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Lautstärke"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Lautsprecher &amp; Displays"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Funktionsweise von Nachrichten an alle"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Nachricht an alle"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personen, die in der Nähe sind und kompatible Bluetooth-Geräten haben, können sich die Medien anhören, die du per Nachricht an alle sendest"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Dieses Display wird dann ausgeschaltet"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Faltbares Gerät wird geöffnet"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Faltbares Gerät wird umgeklappt"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus-Akkustand niedrig"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 05cf06cc4ba0..fce50e56cb30 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Κάτω όριο <xliff:g id="PERCENT">%1$d</xliff:g> τοις εκατό"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Αριστερό όριο <xliff:g id="PERCENT">%1$d</xliff:g> τοις εκατό"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Δεξί όριο <xliff:g id="PERCENT">%1$d</xliff:g> τοις εκατό"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Τα στιγμιότυπα οθόνης εργασίας αποθηκεύονται στην εφαρμογή <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Αρχεία"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Εγγραφή οθόνης"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Επεξεργασία εγγραφής οθόνης"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ειδοποίηση σε εξέλιξη για μια περίοδο λειτουργίας εγγραφής οθόνης"</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Αναπαραγωγή του <xliff:g id="SONG_NAME">%1$s</xliff:g> στην εφαρμογή <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Αναίρεση"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Πλησιάστε για αναπαραγωγή στη συσκευή <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Μετακινηθείτε πιο κοντά στη συσκευή <xliff:g id="DEVICENAME">%1$s</xliff:g> για αναπαραγωγή εδώ"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Αναπαραγωγή στη συσκευή <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Παρουσιάστηκε κάποιο πρόβλημα. Δοκιμάστε ξανά."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Φόρτωση"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Ανενεργό, έλεγχος εφαρμογής"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Δεν βρέθηκε."</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Μη διαθέσιμο στοιχείο ελέγχου"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Ένταση ήχου"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Ηχεία και οθόνες"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Προτεινόμενες συσκευές"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Πώς λειτουργεί η μετάδοση"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Μετάδοση"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Οι άνθρωποι με συμβατές συσκευές Bluetooth που βρίσκονται κοντά σας μπορούν να ακούσουν το μέσο που μεταδίδετε."</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"* Αυτή η οθόνη θα απενεργοποιηθεί"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Αναδιπλούμενη συσκευή που ξεδιπλώνει"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Αναδιπλούμενη συσκευή που διπλώνει"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Χαμηλή στάθμη μπαταρίας γραφίδας"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 7c902807860b..b0421c0818b2 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Bottom boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Left boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Right boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Work screenshots are saved in the <xliff:g id="APP">%1$s</xliff:g> app"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -850,10 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Move closer to <xliff:g id="DEVICENAME">%1$s</xliff:g> to play here"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Something went wrong. Try again."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Loading"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Control is unavailable"</string>
@@ -877,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers &amp; displays"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string>
@@ -1014,5 +1019,6 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string>
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index f195c640bd41..5e564c365944 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Bottom boundary <xliff:g id="PERCENT">%1$d</xliff:g> percent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Left boundary <xliff:g id="PERCENT">%1$d</xliff:g> percent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Right boundary <xliff:g id="PERCENT">%1$d</xliff:g> percent"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Work screenshots are saved in the <xliff:g id="APP">%1$s</xliff:g> app"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -850,10 +852,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Move closer to <xliff:g id="DEVICENAME">%1$s</xliff:g> to play here"</string>
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"To play here, move closer to <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Something went wrong. Try again."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Loading"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Control is unavailable"</string>
@@ -877,6 +880,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers &amp; Displays"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested Devices"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media you\'re broadcasting"</string>
@@ -1014,5 +1018,6 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string>
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 7c902807860b..b0421c0818b2 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Bottom boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Left boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Right boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Work screenshots are saved in the <xliff:g id="APP">%1$s</xliff:g> app"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -850,10 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Move closer to <xliff:g id="DEVICENAME">%1$s</xliff:g> to play here"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Something went wrong. Try again."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Loading"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Control is unavailable"</string>
@@ -877,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers &amp; displays"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string>
@@ -1014,5 +1019,6 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string>
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 7c902807860b..b0421c0818b2 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Bottom boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Left boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Right boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Work screenshots are saved in the <xliff:g id="APP">%1$s</xliff:g> app"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -850,10 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Move closer to <xliff:g id="DEVICENAME">%1$s</xliff:g> to play here"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Something went wrong. Try again."</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"Loading"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Control is unavailable"</string>
@@ -877,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers &amp; displays"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string>
@@ -1014,5 +1019,6 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string>
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 03659b4f7c69..2ab012df6456 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‏‏‎‏‎‏‎‏‏‏‎‎Bottom boundary ‎‏‎‎‏‏‎<xliff:g id="PERCENT">%1$d</xliff:g>‎‏‎‎‏‏‏‎ percent‎‏‎‎‏‎"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‏‎‏‏‎‏‎Left boundary ‎‏‎‎‏‏‎<xliff:g id="PERCENT">%1$d</xliff:g>‎‏‎‎‏‏‏‎ percent‎‏‎‎‏‎"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‏‎‏‎‏‏‎‏‎‏‏‎‎‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎Right boundary ‎‏‎‎‏‏‎<xliff:g id="PERCENT">%1$d</xliff:g>‎‏‎‎‏‏‏‎ percent‎‏‎‎‏‎"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎‏‎‏‎‎‎‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎Work screenshots are saved in the ‎‏‎‎‏‏‎<xliff:g id="APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ app‎‏‎‎‏‎"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‎‎‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‎Files‎‏‎‎‏‎"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‎Screen Recorder‎‏‎‎‏‎"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‎‎Processing screen recording‎‏‎‎‏‎"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‏‎‎‎‏‏‏‏‎‏‏‏‏‎Ongoing notification for a screen record session‎‏‎‎‏‎"</string>
@@ -850,10 +852,11 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‎‏‎‏‎Play ‎‏‎‎‏‏‎<xliff:g id="SONG_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ from ‎‏‎‎‏‏‎<xliff:g id="APP_LABEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎Undo‎‏‎‎‏‎"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‏‏‏‏‎‏‏‏‎‎‎‎Move closer to play on ‎‏‎‎‏‏‎<xliff:g id="DEVICENAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‏‏‎‎‏‎‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎Move closer to ‎‏‎‎‏‏‎<xliff:g id="DEVICENAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to play here‎‏‎‎‏‎"</string>
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‏‎‎‏‎‎‏‎‎To play here, move closer to ‎‏‎‎‏‏‎<xliff:g id="DEVICENAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‏‎‏‎‎Playing on ‎‏‎‎‏‏‎<xliff:g id="DEVICENAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‎‎‏‏‎‎‎‎‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‏‎‏‏‎Something went wrong. Try again.‎‏‎‎‏‎"</string>
<string name="media_transfer_loading" msgid="5544017127027152422">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‏‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‎‏‏‎‎‎‏‎‎‏‏‎‎Loading‎‏‎‎‏‎"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‎‏‎tablet‎‏‎‎‏‎"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‏‏‎‎Inactive, check app‎‏‎‎‏‎"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎Not found‎‏‎‎‏‎"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎‎Control is unavailable‎‏‎‎‏‎"</string>
@@ -877,6 +880,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‎‏‎‎‏‎‎Volume‎‏‎‎‏‎"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‏‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%1$d</xliff:g>‎‏‎‎‏‏‏‎%%‎‏‎‎‏‎"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‎‎‏‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎Speakers &amp; Displays‎‏‎‎‏‎"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎‎‎‏‎‏‎‎‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‎‎‏‎‎‎‎‏‎‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‎‎Suggested Devices‎‏‎‎‏‎"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‎‎How broadcasting works‎‏‎‎‏‎"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‎‎‎‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‏‏‎Broadcast‎‏‎‎‏‎"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‎‎People near you with compatible Bluetooth devices can listen to the media you\'re broadcasting‎‏‎‎‏‎"</string>
@@ -1014,5 +1018,6 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎✱ This screen will turn off‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎Foldable device being unfolded‎‏‎‎‏‎"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‏‏‎‎‏‎‎‎‎‎Foldable device being flipped around‎‏‎‎‏‎"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‎‎‏‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‎‏‏‏‎‎‏‎‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‎Stylus battery low‎‏‎‎‏‎"</string>
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‎‏‎‏‎‏‏‏‎‎‏‎‎‏‎‎‎‏‎‏‎‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ battery remaining‎‏‎‎‏‎"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎Connect your stylus to a charger‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 2dc01f110b2c..3d6419dba9eb 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Límite inferior: <xliff:g id="PERCENT">%1$d</xliff:g> por ciento"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Límite izquierdo: <xliff:g id="PERCENT">%1$d</xliff:g> por ciento"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Límite derecho: <xliff:g id="PERCENT">%1$d</xliff:g> por ciento"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Grabadora de pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando grabación pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación constante para una sesión de grabación de pantalla"</string>
@@ -375,7 +379,7 @@
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"Pantalla completa"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"Una sola app"</string>
<string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"Cuando compartas, grabes o transmitas contenido, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> podrá acceder a todo aquel que sea visible en la pantalla o que reproduzcas en el dispositivo. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes y otra información sensible."</string>
- <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Cuando compartas, grabes o transmitas una app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> podrá acceder a todo el contenido que se muestre o reproduzca en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes y otra información sensible."</string>
+ <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Cuando compartas, grabes o transmitas una app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> podrá acceder a todo el contenido que se muestre o reproduzca en ella. Por lo tanto, debes tener cuidado con las contraseñas, los detalles de pagos, los mensajes y otra información sensible."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Continuar"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Compartir o grabar una app"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"¿Quieres permitir que esta app comparta o grabe tu pantalla?"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproducir <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Deshacer"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Acércate para reproducir en <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Acércate a <xliff:g id="DEVICENAME">%1$s</xliff:g> para reproducir aquí"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Reproduciendo en <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Se produjo un error. Vuelve a intentarlo."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Cargando"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Verifica la app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"No se encontró"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volumen"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Bocinas y pantallas"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la transmisión"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transmisión"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Las personas cercanas con dispositivos Bluetooth compatibles pueden escuchar el contenido multimedia que transmites"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Esta pantalla se apagará"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo plegable siendo desplegado"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo plegable siendo girado"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"La pluma stylus tiene poca batería"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index c2443daccceb..9839e509acbf 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"<xliff:g id="PERCENT">%1$d</xliff:g> por ciento del límite inferior"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"<xliff:g id="PERCENT">%1$d</xliff:g> por ciento del límite izquierdo"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"<xliff:g id="PERCENT">%1$d</xliff:g> por ciento del límite derecho"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Grabación de pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando grabación de pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación continua de una sesión de grabación de la pantalla"</string>
@@ -849,11 +853,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Poner <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> en <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Poner <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Deshacer"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Acércate a <xliff:g id="DEVICENAME">%1$s</xliff:g> para que se reproduzca en ese dispositivo"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Acércate a <xliff:g id="DEVICENAME">%1$s</xliff:g> para jugar aquí"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Acércate a <xliff:g id="DEVICENAME">%1$s</xliff:g> para reproducir contenido ahí"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Reproduciendo en <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Se ha producido un error. Inténtalo de nuevo."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Cargando"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Inactivo, comprobar aplicación"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"No se ha encontrado"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volumen"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altavoces y pantallas"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la emisión"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Emisión"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Las personas cercanas con dispositivos Bluetooth compatibles pueden escuchar el contenido multimedia que emites"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Esta pantalla se apagará"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo plegable desplegándose"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo plegable mostrado desde varios ángulos"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Batería del lápiz óptico baja"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index b31030a42fe3..5a83eb6fb60a 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Alapiir: <xliff:g id="PERCENT">%1$d</xliff:g> protsenti"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Vasak piir: <xliff:g id="PERCENT">%1$d</xliff:g> protsenti"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Parem piir: <xliff:g id="PERCENT">%1$d</xliff:g> protsenti"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Ekraanisalvesti"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekraanisalvestuse töötlemine"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pooleli märguanne ekraanikuva salvestamise seansi puhul"</string>
@@ -849,11 +853,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Esita lugu <xliff:g id="SONG_NAME">%1$s</xliff:g> esitajalt <xliff:g id="ARTIST_NAME">%2$s</xliff:g> rakenduses <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Esita lugu <xliff:g id="SONG_NAME">%1$s</xliff:g> rakenduses <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Võta tagasi"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Teisaldage lähemale, et seadmes <xliff:g id="DEVICENAME">%1$s</xliff:g> esitada"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Siin esitamiseks liigutage seadmele <xliff:g id="DEVICENAME">%1$s</xliff:g> lähemale"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Liikuge lähemale, et seadmes <xliff:g id="DEVICENAME">%1$s</xliff:g> esitada"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Esitatakse seadmes <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Midagi läks valesti. Proovige uuesti."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Laadimine"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Passiivne, vaadake rakendust"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ei leitud"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Helitugevus"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Kõlarid ja ekraanid"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kuidas ülekandmine toimib?"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Ülekanne"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Teie läheduses olevad inimesed, kellel on ühilduvad Bluetooth-seadmed, saavad kuulata teie ülekantavat meediat"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ See ekraan lülitatakse välja"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Volditava seadme lahtivoltimine"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Volditava seadme ümberpööramine"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Elektronpliiatsi akutase on madal"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index de9c75f45af8..38db7066608c 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Beheko ertza: ehuneko <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Ezkerreko ertza: ehuneko <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Eskuineko ertza: ehuneko <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Pantaila-grabagailua"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Pantaila-grabaketa prozesatzen"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pantailaren grabaketa-saioaren jakinarazpen jarraitua"</string>
@@ -690,7 +694,7 @@
<string name="tuner_lock_screen" msgid="2267383813241144544">"Pantaila blokeatua"</string>
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Beroegi egoteagatik itzali da"</string>
<string name="thermal_shutdown_message" msgid="6142269839066172984">"Ohi bezala ari da funtzionatzen telefonoa orain.\nInformazio gehiago lortzeko, sakatu hau."</string>
- <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefonoa gehiegi berotu da, eta itzali egin da tenperatura jaisteko. Orain, ohiko moduan dabil.\n\nBerotzearen zergati posibleak:\n • Baliabide asko behar dituzten aplikazioak erabiltzea (adib., jokoak, bideoak edo nabigazio-aplikazioak).\n • Fitxategi handiak deskargatu edo kargatzea.\n • Telefonoa giro beroetan erabiltzea."</string>
+ <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefonoa gehiegi berotu da, eta itzali egin da tenperatura jaisteko. Orain, ohiko moduan dabil.\n\nBerotzearen zergati posibleak:\n • Baliabide asko behar dituzten aplikazioak erabiltzea (adib., bideojokoak, bideoak edo nabigazio-aplikazioak).\n • Fitxategi handiak deskargatu edo kargatzea.\n • Telefonoa giro beroetan erabiltzea."</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ikusi zaintzeko urratsak"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Berotzen ari da telefonoa"</string>
<string name="high_temp_notif_message" msgid="1277346543068257549">"Eginbide batzuk ezingo dira erabili telefonoa hoztu arte.\nInformazio gehiago lortzeko, sakatu hau."</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Erreproduzitu <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g> bidez"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desegin"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Gertura ezazu <xliff:g id="DEVICENAME">%1$s</xliff:g> gailuan erreproduzitzeko"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Gerturatu <xliff:g id="DEVICENAME">%1$s</xliff:g> gailura bertan erreproduzitzen ari dena hemen erreproduzitzeko"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> gailuan erreproduzitzen"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Arazoren bat izan da. Saiatu berriro."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Kargatzen"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktibo; egiaztatu aplikazioa"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ez da aurkitu"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Bolumena"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%% <xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Bozgorailuak eta pantailak"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Nola funtzionatzen dute iragarpenek?"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Iragarri"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Bluetooth bidezko gailu bateragarriak dituzten inguruko pertsonek iragartzen ari zaren multimedia-edukia entzun dezakete"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Pantaila itzali egingo da"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Gailu tolesgarria zabaltzen"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Gailu tolesgarria biratzen"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Arkatzak bateria gutxi du"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 87aed942d05e..9800da9b78aa 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"مرز پایین <xliff:g id="PERCENT">%1$d</xliff:g> درصد"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"مرز سمت چپ <xliff:g id="PERCENT">%1$d</xliff:g> درصد"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"مرز سمت راست <xliff:g id="PERCENT">%1$d</xliff:g> درصد"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"ضبط‌کننده صفحه‌نمایش"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"درحال پردازش ضبط صفحه‌نمایش"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"اعلان درحال انجام برای جلسه ضبط صفحه‌نمایش"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> را ازطریق <xliff:g id="APP_LABEL">%2$s</xliff:g> پخش کنید"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"واگرد"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"برای پخش در <xliff:g id="DEVICENAME">%1$s</xliff:g> به دستگاه نزدیک‌تر شوید"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"برای پخش در اینجا، به <xliff:g id="DEVICENAME">%1$s</xliff:g> نزدیک‌تر شوید"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"درحال پخش در <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"مشکلی پیش آمد. دوباره امتحان کنید."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"درحال بار کردن"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"غیرفعال، برنامه را بررسی کنید"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"پیدا نشد"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"میزان صدا"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"بلندگوها و نمایشگرها"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"همه‌فرتستی چطور کار می‌کند"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"همه‌فرستی"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"‏افرادی که در اطرافتان دستگاه‌های Bluetooth سازگار دارند می‌توانند به رسانه‌ای که همه‌فرستی می‌کنید گوش کنند"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ این صفحه‌نمایش خاموش خواهد شد"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"دستگاه تاشو درحال باز شدن"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"دستگاه تاشو درحال چرخش به اطراف"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"باتری قلم ضعیف است"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index f7bb7f701687..efdc9cb89e17 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Alareuna <xliff:g id="PERCENT">%1$d</xliff:g> prosenttia"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Vasen reuna <xliff:g id="PERCENT">%1$d</xliff:g> prosenttia"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Oikea reuna <xliff:g id="PERCENT">%1$d</xliff:g> prosenttia"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Näytön tallentaja"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Näytön tallennusta käsitellään"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pysyvä ilmoitus näytön tallentamisesta"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Soita <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="APP_LABEL">%2$s</xliff:g>)"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Kumoa"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Siirry lähemmäs, jotta <xliff:g id="DEVICENAME">%1$s</xliff:g> voi toistaa tämän"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Siirrä <xliff:g id="DEVICENAME">%1$s</xliff:g> lähemmäs toistaaksesi täällä"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Toistetaan: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Jotain meni pieleen. Yritä uudelleen."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Latautuminen"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Epäaktiivinen, tarkista sovellus"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ei löydy"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Äänenvoimakkuus"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Kaiuttimet ja näytöt"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Miten lähetys toimii"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Lähetys"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Lähistöllä olevat ihmiset, joilla on yhteensopiva Bluetooth-laite, voivat kuunnella lähettämääsi mediaa"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Tämä näyttö sammutetaan"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Taitettava laite taitetaan"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Taitettava laite käännetään ympäri"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Näyttökynän akku vähissä"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index be81a9275544..5423c579bf2b 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Limite inférieure : <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Limite gauche : <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Limite droite : <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Trait. de l\'enregist. d\'écran…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement d\'écran"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Lecture de <xliff:g id="SONG_NAME">%1$s</xliff:g> à partir de <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Annuler"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Rapprochez-vous pour faire jouer le contenu sur <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Rapprochez-vous de <xliff:g id="DEVICENAME">%1$s</xliff:g> pour lire le contenu"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Lecture sur <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Un problème est survenu. Réessayez."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Chargement en cours…"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifiez l\'appli"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Introuvable"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Haut-parleurs et écrans"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement de la diffusion"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Diffusion"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les personnes à proximité disposant d\'appareils Bluetooth compatibles peuvent écouter le contenu multimédia que vous diffusez"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"* Cet écran va s\'éteindre"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Appareil pliable en cours de dépliage"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Appareil pliable en train d\'être retourné"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Pile du stylet faible"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 869166321079..c356d7393c12 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Limite inférieure : <xliff:g id="PERCENT">%1$d</xliff:g> pour cent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Limite gauche : <xliff:g id="PERCENT">%1$d</xliff:g> pour cent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Limite droite : <xliff:g id="PERCENT">%1$d</xliff:g> pour cent"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Enregistrement de l\'écran…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement de l\'écran"</string>
@@ -849,11 +853,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Mets <xliff:g id="SONG_NAME">%1$s</xliff:g> par <xliff:g id="ARTIST_NAME">%2$s</xliff:g> depuis <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Mets <xliff:g id="SONG_NAME">%1$s</xliff:g> depuis <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Annuler"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Rapprochez-vous pour lire sur <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Rapprochez l\'appareil pour transférer la diffusion à votre <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Lecture sur <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Rapprochez-vous de votre <xliff:g id="DEVICENAME">%1$s</xliff:g> pour y lire le contenu"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
+ <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Lecture sur <xliff:g id="DEVICENAME">%1$s</xliff:g>…"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Un problème est survenu. Réessayez."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Chargement…"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifier l\'appli"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Introuvable"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Enceintes et écrans"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement des annonces"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Annonce"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les personnes à proximité équipées d\'appareils Bluetooth compatibles peuvent écouter le contenu multimédia que vous diffusez"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Cet écran sera désactivé"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Appareil pliable qui est déplié"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Appareil pliable qui est retourné"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"La batterie du stylet est faible"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index f9738e3b413a..4bc51230c08b 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Bordo inferior: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Bordo esquerdo: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Bordo dereito: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Gravadora da pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando gravación pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación en curso sobre unha sesión de gravación de pantalla"</string>
@@ -383,7 +387,7 @@
<string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"Cando compartes, gravas ou emites aplicacións, esta aplicación ten acceso a todo o que se vexa ou se reproduza nelas. Polo tanto, debes ter coidado cos contrasinais, os detalles de pago, as mensaxes ou outra información confidencial."</string>
<string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"O teu administrador de TI bloqueou esta aplicación"</string>
<string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"A política do dispositivo desactivou a opción de capturar a pantalla"</string>
- <string name="clear_all_notifications_text" msgid="348312370303046130">"Eliminar todas"</string>
+ <string name="clear_all_notifications_text" msgid="348312370303046130">"Eliminar todo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Xestionar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Notificacións novas"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproduce <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desfacer"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Achega o dispositivo para reproducir o contido en: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Achégate ao dispositivo (<xliff:g id="DEVICENAME">%1$s</xliff:g>) para reproducir o contido neste"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Reproducindo contido noutro dispositivo (<xliff:g id="DEVICENAME">%1$s</xliff:g>)"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Produciuse un erro. Téntao de novo."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Cargando"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Comproba a app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Non se atopou"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altofalantes e pantallas"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funcionan as difusións?"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Difusión"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As persoas que estean preto de ti e que dispoñan de dispositivos Bluetooth compatibles poden escoitar o contido multimedia que difundas"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Desactivarase esta pantalla"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo pregable abríndose"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo pregable xirando"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"O lapis óptico ten pouca batería"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index dac1403f8dd5..3075f16860d2 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"નીચેની સીમા <xliff:g id="PERCENT">%1$d</xliff:g> ટકા"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ડાબી બાજુની સીમા <xliff:g id="PERCENT">%1$d</xliff:g> ટકા"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"જમણી બાજુની સીમા <xliff:g id="PERCENT">%1$d</xliff:g> ટકા"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"સ્ક્રીન રેકોર્ડર"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"સ્ક્રીન રેકૉર્ડિંગ ચાલુ છે"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"સ્ક્રીન રેકોર્ડિંગ સત્ર માટે ચાલુ નોટિફિકેશન"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> પર <xliff:g id="SONG_NAME">%1$s</xliff:g> ગીત ચલાવો"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"છેલ્લો ફેરફાર રદ કરો"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> પર ચલાવવા માટે વધુ નજીક ખસેડો"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"આમાં ચલાવવા માટે ડિવાઇસને <xliff:g id="DEVICENAME">%1$s</xliff:g>ની નજીક ખસેડો"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> પર ચલાવવામાં આવી રહ્યું છે"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"કંઈક ખોટું થયું. ફરી પ્રયાસ કરો."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"લોડ થઈ રહ્યું છે"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"નિષ્ક્રિય, ઍપને ચેક કરો"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"મળ્યું નથી"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"વૉલ્યૂમ"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"સ્પીકર અને ડિસ્પ્લે"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"બ્રોડકાસ્ટ પ્રક્રિયાની કામ કરવાની રીત"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"બ્રોડકાસ્ટ કરો"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"સુસંગત બ્લૂટૂથ ડિવાઇસ ધરાવતા નજીકના લોકો તમે જે મીડિયા બ્રોડકાસ્ટ કરી રહ્યાં છો તે સાંભળી શકે છે"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ આ સ્ક્રીન બંધ થઈ જશે"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ફોલ્ડ કરી શકાય એવું ડિવાઇસ અનફોલ્ડ કરવામાં આવી રહ્યું છે"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ફોલ્ડ કરી શકાય એવું ડિવાઇસ ફ્લિપ કરવામાં આવી રહ્યું છે"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"સ્ટાઇલસની બૅટરીમાં ચાર્જ ઓછો છે"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 38d2bffa239a..6828e8d4893d 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"निचले किनारे से <xliff:g id="PERCENT">%1$d</xliff:g> प्रतिशत"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"बाएं किनारे से <xliff:g id="PERCENT">%1$d</xliff:g> प्रतिशत"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"दाएं किनारे से <xliff:g id="PERCENT">%1$d</xliff:g> प्रतिशत"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रिकॉर्डर"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रीन रिकॉर्डिंग को प्रोसेस किया जा रहा है"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"स्क्रीन रिकॉर्ड सेशन के लिए जारी सूचना"</string>
@@ -371,11 +375,11 @@
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"इस फ़ंक्शन को उपलब्ध कराने वाली सेवा, रिकॉर्ड या कास्ट करते समय, आपकी स्क्रीन पर दिखने वाली या चलाई जाने वाली जानकारी को ऐक्सेस कर सकती है. इसमें पासवर्ड, पैसे चुकाने से जुड़ी जानकारी, फ़ोटो, मैसेज, और चलाए जाने वाले ऑडियो शामिल हैं."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"रिकॉर्डिंग या कास्ट करना शुरू करें?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> का इस्तेमाल करके रिकॉर्ड और कास्ट करना शुरू करें?"</string>
- <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> को शेयर या रिकॉर्ड करने की अनुमति दें?"</string>
+ <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"क्या आपको शेयर या रिकॉर्ड करने की <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> को अनुमति देनी है?"</string>
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"पूरी स्क्रीन"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"सिर्फ़ एक ऐप्लिकेशन"</string>
<string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"शेयर, रिकॉर्ड या कास्ट करते समय, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> के पास स्क्रीन पर दिख रही हर चीज़ या डिवाइस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, शेयर, रिकॉर्ड या कास्ट करते समय, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज या किसी और संवेदनशील जानकारी को लेकर खास सावधानी बरतें."</string>
- <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"शेयर, रिकॉर्ड या कास्ट करते समय, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> के पास उस ऐप्लिकेशन पर दिख रही हर चीज़ या उस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, शेयर, रिकॉर्ड या कास्ट करते समय, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज या किसी और संवेदनशील जानकारी को लेकर खास सावधानी बरतें."</string>
+ <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"शेयर, रिकॉर्ड या कास्ट करते समय, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> के पास उस ऐप्लिकेशन पर दिख रही हर चीज़ या उस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज या किसी और संवेदनशील जानकारी को लेकर खास सावधानी बरतें."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"जारी रखें"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"ऐप्लिकेशन शेयर करें या उसकी रिकॉर्डिंग करें"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"क्या इस ऐप्लिकेशन को शेयर या रिकॉर्ड करने की अनुमति देनी है?"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> पर, <xliff:g id="SONG_NAME">%1$s</xliff:g> चलाएं"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"पहले जैसा करें"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> पर मीडिया चलाने के लिए, अपने डिवाइस को उसके पास ले जाएं"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"अपने डिवाइस पर मीडिया फ़ाइल ट्रांसफ़र करने के लिए, उसे <xliff:g id="DEVICENAME">%1$s</xliff:g> के पास ले जाएं"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> पर मीडिया चल रहा है"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"कोई गड़बड़ी हुई. फिर से कोशिश करें."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"लोड हो रहा है"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"काम नहीं कर रहा, ऐप जांचें"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"कंट्रोल नहीं है"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"वॉल्यूम"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"स्पीकर और डिसप्ले"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ब्रॉडकास्ट करने की सुविधा कैसे काम करती है"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ब्रॉडकास्ट करें"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"आपके आस-पास मौजूद लोग, ब्रॉडकास्ट किए जा रहे मीडिया को सुन सकते हैं. हालांकि, इसके लिए उनके पास ऐसे ब्लूटूथ डिवाइस होने चाहिए जिन पर मीडिया चलाया जा सके"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ यह स्क्रीन बंद हो जाएगी"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"फ़ोल्ड किया जा सकने वाला डिवाइस अनफ़ोल्ड किया जा रहा है"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"फ़ोल्ड किया जा सकने वाला डिवाइस पलटा जा रहा है"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलस की बैटरी कम है"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 7613317734d1..484e3a4577c2 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Donji rub <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Lijevi rub <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Desni rub <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Snimke zaslona s poslovnog profila spremaju se u aplikaciju <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Datoteke"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snimač zaslona"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrada snimanja zaslona"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Tekuća obavijest za sesiju snimanja zaslona"</string>
@@ -377,7 +379,7 @@
<string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"Kad dijelite, snimate ili emitirate, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup svemu što je vidljivo na vašem zaslonu ili se reproducira na vašem uređaju. Stoga pazite na zaporke, podatke o plaćanju, poruke i druge osjetljive podatke."</string>
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Kad dijelite, snimate ili emitirate aplikaciju, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga pazite na zaporke, podatke o plaćanju, poruke i druge osjetljive podatke."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Nastavi"</string>
- <string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Dijeljenje ili snimanje pomoću aplikacije"</string>
+ <string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Dijeljenje ili snimanje aplikacije"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"Želite li ovoj aplikaciji omogućiti dijeljenje ili bilježenje?"</string>
<string name="media_projection_permission_dialog_system_service_warning_entire_screen" msgid="8801616203805837575">"Kad dijelite, snimate ili emitirate, ova aplikacija ima pristup svemu što je vidljivo na vašem zaslonu ili se reproducira na vašem uređaju. Stoga pazite na zaporke, podatke o plaćanju, poruke i druge osjetljive podatke."</string>
<string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"Kad dijelite, snimate ili emitirate aplikaciju, ova aplikacija ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga pazite na zaporke, podatke o plaćanju, poruke i druge osjetljive podatke."</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g> putem aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Poništi"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Približite se radi reprodukcije na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Približite se uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g> da biste na njemu reproducirali"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Reproducira se na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Nešto nije u redu. Pokušajte ponovo."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Učitavanje"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, provjerite aplik."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrola nije dostupna"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Glasnoća"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvučnici i zasloni"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Predloženi uređaji"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako emitiranje funkcionira"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Emitiranje"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osobe u blizini s kompatibilnim Bluetooth uređajima mogu slušati medije koje emitirate"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ovaj će se zaslon isključiti"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Rasklopljen sklopivi uređaj"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Okretanje sklopivog uređaja sa svih strana"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Slaba baterija pisaljke"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index c2b94c0b1102..cfa0577c29be 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Alsó rész <xliff:g id="PERCENT">%1$d</xliff:g> százaléka"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Bal oldali rész <xliff:g id="PERCENT">%1$d</xliff:g> százaléka"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Jobb oldali rész <xliff:g id="PERCENT">%1$d</xliff:g> százaléka"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Képernyőrögzítő"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Képernyőrögzítés feldolgozása"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Folyamatban lévő értesítés képernyőrögzítési munkamenethez"</string>
@@ -849,11 +853,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> <xliff:g id="SONG_NAME">%1$s</xliff:g> című számának lejátszása innen: <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> lejátszása innen: <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Visszavonás"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Menjen közelebb a következőn való lejátszáshoz: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Menjen közelebb a(z) <xliff:g id="DEVICENAME">%1$s</xliff:g> eszközhöz, hogy itt játszhassa le a tartalmat"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Menjen közelebb, ha itt szeretné lejátszani: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Lejátszás folyamatban a(z) <xliff:g id="DEVICENAME">%1$s</xliff:g> eszközön"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Hiba történt. Próbálkozzon újra."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Betöltés…"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktív, ellenőrizze az appot"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nem található"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Hangerő"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Hangfalak és kijelzők"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"A közvetítés működése"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Közvetítés"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"A közelben tartózkodó, kompatibilis Bluetooth-eszközzel rendelkező személyek meghallgathatják az Ön közvetített médiatartalmait"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ A képernyő kikapcsol"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Összehajtható eszköz kihajtása"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Összehajtható eszköz körbeforgatása"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Az érintőceruza töltöttsége alacsony"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 5623ecfb7f29..8f7ecf600138 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Ներքևի սահմանագիծը՝ <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Ձախ կողմի սահմանագիծը՝ <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Աջ կողմի սահմանագիծը՝ <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Էկրանի տեսագրիչ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Էկրանի տեսագրության մշակում"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Էկրանի տեսագրման աշխատաշրջանի ընթացիկ ծանուցում"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Նվագարկել <xliff:g id="SONG_NAME">%1$s</xliff:g> երգը <xliff:g id="APP_LABEL">%2$s</xliff:g> հավելվածից"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Հետարկել"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Ավելի մոտ եկեք՝ <xliff:g id="DEVICENAME">%1$s</xliff:g> սարքում նվագարկելու համար"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Ավելի մոտեցեք «<xliff:g id="DEVICENAME">%1$s</xliff:g>» սարքին՝ նվագարկումը սկսելու համար"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Նվագարկվում է «<xliff:g id="DEVICENAME">%1$s</xliff:g>» սարքում"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Սխալ առաջացավ։ Նորից փորձեք։"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Բեռնվում է"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Ակտիվ չէ, ստուգեք հավելվածը"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Չի գտնվել"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Ձայնի ուժգնություն"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Բարձրախոսներ և էկրաններ"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Ինչպես է աշխատում հեռարձակումը"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Հեռարձակում"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Ձեր մոտակայքում գտնվող՝ համատեղելի Bluetooth սարքերով մարդիկ կարող են լսել մեդիա ֆայլերը, որոնք դուք հեռարձակում եք։"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Այս էկրանը կանջատվի"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Ծալովի սարք՝ բացված վիճակում"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Ծալովի սարք՝ շրջված վիճակում"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Ստիլուսի մարտկոցի լիցքի ցածր մակարդակ"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index bf5f8b01848c..a427a2c56f55 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Batas bawah <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Batas kiri <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Batas kanan <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Perekam Layar"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses perekaman layar"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifikasi yang sedang berjalan untuk sesi rekaman layar"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Putar <xliff:g id="SONG_NAME">%1$s</xliff:g> dari <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Urungkan"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Dekatkan untuk memutar di <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Dekatkan ke <xliff:g id="DEVICENAME">%1$s</xliff:g> untuk memutar di sini"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Diputar di <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Terjadi error. Coba lagi."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Memuat"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Nonaktif, periksa aplikasi"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Tidak ditemukan"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speaker &amp; Layar"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cara kerja siaran"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Siaran"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Orang di dekat Anda dengan perangkat Bluetooth yang kompatibel dapat mendengarkan media yang sedang Anda siarkan"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Layar ini akan dinonaktifkan"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Perangkat foldable sedang dibentangkan"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Perangkat foldable sedang dibalik"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Baterai stilus lemah"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index f3efb8624333..c12fb4822cf9 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Neðri mörk <xliff:g id="PERCENT">%1$d</xliff:g> prósent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Vinstri mörk <xliff:g id="PERCENT">%1$d</xliff:g> prósent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Hægri mörk <xliff:g id="PERCENT">%1$d</xliff:g> prósent"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Skjáupptaka"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Vinnur úr skjáupptöku"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Áframhaldandi tilkynning fyrir skjáupptökulotu"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spila <xliff:g id="SONG_NAME">%1$s</xliff:g> í <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Afturkalla"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Færðu nær til að spila í <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Færðu tækið nær <xliff:g id="DEVICENAME">%1$s</xliff:g> til að spila hér"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Í spilun í <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Eitthvað fór úrskeiðis. Reyndu aftur."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Hleður"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Óvirkt, athugaðu forrit"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Fannst ekki"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Hljóðstyrkur"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Hátalarar og skjáir"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Svona virkar útsending"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Útsending"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Fólk nálægt þér með samhæf Bluetooth-tæki getur hlustað á efnið sem þú sendir út"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Slökkt verður á þessum skjá"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Samanbrjótanlegt tæki opnað"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Samanbrjótanlegu tæki snúið við"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Rafhlaða pennans er að tæmast"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index e8c6a5b21308..5c52bc13974e 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Limite inferiore, <xliff:g id="PERCENT">%1$d</xliff:g> percento"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Limite sinistro, <xliff:g id="PERCENT">%1$d</xliff:g> percento"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Limite destro, <xliff:g id="PERCENT">%1$d</xliff:g> percento"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Registrazione dello schermo"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Elaboraz. registraz. schermo"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifica costante per una sessione di registrazione dello schermo"</string>
@@ -371,7 +375,7 @@
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Il servizio che offre questa funzione avrà accesso a tutte le informazioni visibili sul tuo schermo o riprodotte dal tuo dispositivo durante la registrazione o la trasmissione. Sono incluse informazioni quali password, dettagli sui pagamenti, foto, messaggi e audio riprodotto."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Vuoi avviare la registrazione o la trasmissione?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Vuoi avviare la registrazione o la trasmissione con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"Consenti a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> di condividere o registrare?"</string>
+ <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"Consentire a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> di condividere o registrare?"</string>
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"Schermo intero"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"Una sola app"</string>
<string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"Quando condividi, registri o trasmetti, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ha accesso a qualsiasi elemento visibile sul tuo schermo o in riproduzione sul tuo dispositivo. Presta quindi attenzione a password, dati di pagamento, messaggi o altre informazioni sensibili."</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Riproduci <xliff:g id="SONG_NAME">%1$s</xliff:g> da <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Annulla"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Avvicinati per riprodurre su <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Avvicinati a <xliff:g id="DEVICENAME">%1$s</xliff:g> per riprodurre i contenuti qui"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"In riproduzione su <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Si è verificato un errore. Riprova."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Caricamento in corso…"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Inattivo, controlla l\'app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Controllo non trovato"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speaker e display"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Come funziona la trasmissione"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Annuncio"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Le persone vicine a te che hanno dispositivi Bluetooth compatibili possono ascoltare i contenuti multimediali che stai trasmettendo"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Questo schermo verrà disattivato"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo pieghevole che viene aperto"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo pieghevole che viene capovolto"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Batteria stilo in esaurimento"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 690b6b22710c..de95f2e372ae 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"<xliff:g id="PERCENT">%1$d</xliff:g> אחוז מהשוליים התחתונים"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"<xliff:g id="PERCENT">%1$d</xliff:g> אחוז מהשוליים השמאליים"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"<xliff:g id="PERCENT">%1$d</xliff:g> אחוז מהשוליים הימניים"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"צילומי מסך בפרופיל העבודה נשמרים באפליקציה <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"קבצים"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"מקליט המסך"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"מתבצע עיבוד של הקלטת מסך"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"התראה מתמשכת לסשן הקלטת מסך"</string>
@@ -98,8 +100,8 @@
<string name="screenrecord_description" msgid="1123231719680353736">"‏בזמן ההקלטה, מערכת Android יכולה לתעד מידע רגיש שגלוי במסך או מופעל במכשיר שלך. מידע זה כולל סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו."</string>
<string name="screenrecord_option_entire_screen" msgid="1732437834603426934">"הקלטה של כל המסך"</string>
<string name="screenrecord_option_single_app" msgid="5954863081500035825">"הקלטה של אפליקציה אחת"</string>
- <string name="screenrecord_warning_entire_screen" msgid="8141407178104195610">"‏בזמן ההקלטה, תהיה ל-Android גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך. כדאי להיזהר עם סיסמאות, פרטי תשלום, הודעות או מידע רגיש אחר."</string>
- <string name="screenrecord_warning_single_app" msgid="7760723997065948283">"‏בזמן הקלטה של אפליקציה, תהיה ל-Android גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. כדאי להיזהר עם סיסמאות, פרטי תשלום, הודעות או מידע רגיש אחר."</string>
+ <string name="screenrecord_warning_entire_screen" msgid="8141407178104195610">"‏בזמן ההקלטה, תהיה ל-Android גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך. חשוב להיזהר עם סיסמאות, פרטי תשלום, הודעות או מידע רגיש אחר."</string>
+ <string name="screenrecord_warning_single_app" msgid="7760723997065948283">"‏בזמן הקלטה של אפליקציה, תהיה ל-Android גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. חשוב להיזהר עם סיסמאות, פרטי תשלום, הודעות או מידע רגיש אחר."</string>
<string name="screenrecord_start_recording" msgid="348286842544768740">"התחלת ההקלטה"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"הקלטת אודיו"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"אודיו מהמכשיר"</string>
@@ -374,8 +376,8 @@
<string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"לאפשר לאפליקציה <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> לשתף או להקליט?"</string>
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"כל המסך"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"אפליקציה אחת"</string>
- <string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"‏בזמן שיתוף, הקלטה או העברה (cast) תהיה ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך. כדאי להיזהר עם סיסמאות, פרטי תשלום, הודעות או מידע רגיש אחר."</string>
- <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"‏בזמן שיתוף, הקלטה או העברה (cast) של אפליקציה, תהיה ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. כדאי להיזהר עם סיסמאות, פרטי תשלום, הודעות או מידע רגיש אחר."</string>
+ <string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"‏בזמן שיתוף, הקלטה או העברה (cast) תהיה ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך. חשוב להיזהר עם סיסמאות, פרטי תשלום, הודעות או מידע רגיש אחר."</string>
+ <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"‏בזמן שיתוף, הקלטה או העברה (cast) של אפליקציה, תהיה ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. חשוב להיזהר עם סיסמאות, פרטי תשלום, הודעות או מידע רגיש אחר."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"המשך"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"שיתוף או הקלטה של אפליקציה"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"לאפשר לאפליקציה הזו לשתף או להקליט?"</string>
@@ -849,12 +851,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"הפעלת <xliff:g id="SONG_NAME">%1$s</xliff:g> של <xliff:g id="ARTIST_NAME">%2$s</xliff:g> מ-<xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"הפעלת <xliff:g id="SONG_NAME">%1$s</xliff:g> מ-<xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ביטול"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"צריך להתקרב כדי להפעיל מוזיקה במכשיר <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"צריך להתקרב אל <xliff:g id="DEVICENAME">%1$s</xliff:g> כדי להפעיל כאן"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"צריך להתקרב כדי להפעיל מדיה במכשיר <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"פועלת ב-<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"משהו השתבש. יש לנסות שוב."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"בטעינה"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"טאבלט"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"לא פעיל, יש לבדוק את האפליקציה"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"לא נמצא"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"הפקד לא זמין"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"עוצמת הקול"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"‎<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%‎‎"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"רמקולים ומסכים"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"הצעות למכשירים"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"הסבר על שידורים"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"שידור"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"‏אנשים בקרבת מקום עם מכשירי Bluetooth תואמים יכולים להאזין למדיה שמשודרת על ידך"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ המסך יכבה"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"מכשיר מתקפל עובר למצב לא מקופל"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"מכשיר מתקפל עובר למצב מהופך"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"הסוללה של הסטיילוס חלשה"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 5bd580ccc603..3d4551c4b747 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"下部の境界線 <xliff:g id="PERCENT">%1$d</xliff:g> パーセント"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"左の境界線 <xliff:g id="PERCENT">%1$d</xliff:g> パーセント"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"右の境界線 <xliff:g id="PERCENT">%1$d</xliff:g> パーセント"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"スクリーン レコーダー"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"画面の録画を処理しています"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"画面の録画セッション中の通知"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> を <xliff:g id="APP_LABEL">%2$s</xliff:g> で再生"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"元に戻す"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>で再生するにはもっと近づけてください"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"ここで再生するには<xliff:g id="DEVICENAME">%1$s</xliff:g>に近づいてください"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g>で再生しています"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"エラーが発生しました。もう一度お試しください。"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"読み込んでいます"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"無効: アプリをご確認ください"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"見つかりませんでした"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"音量"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"スピーカーとディスプレイ"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ブロードキャストの仕組み"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ブロードキャスト"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Bluetooth 対応デバイスを持っている付近のユーザーは、あなたがブロードキャストしているメディアを聴けます"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱この画面は OFF になります"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"折りたたみ式デバイスが広げられている"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"折りたたみ式デバイスがひっくり返されている"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"タッチペンのバッテリー残量が少なくなっています"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index d25daf440ce2..61280d3d425f 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"ქვედა ზღვარი: <xliff:g id="PERCENT">%1$d</xliff:g> პროცენტი"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"მარცხენა ზღვარი: <xliff:g id="PERCENT">%1$d</xliff:g> პროცენტი"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"მარჯვენა ზღვარი: <xliff:g id="PERCENT">%1$d</xliff:g> პროცენტი"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"სამუშაო ეკრანის ანაბეჭდები ინახება <xliff:g id="APP">%1$s</xliff:g> აპში"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ფაილები"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ეკრანის ჩამწერი"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ეკრანის ჩანაწერი მუშავდება"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"უწყვეტი შეტყობინება ეკრანის ჩაწერის სესიისთვის"</string>
@@ -98,7 +100,7 @@
<string name="screenrecord_description" msgid="1123231719680353736">"ჩაწერის განმავლობაში Android სისტემას შეუძლია აღბეჭდოს ნებისმიერი სენსიტიური ინფორმაცია, რომელიც თქვენს ეკრანზე გამოჩნდება ან თქვენს მოწყობილობაზე დაიკვრება. აღნიშნული მოიცავს პაროლებს, გადახდის დეტალებს, ფოტოებს, შეტყობინებებსა და აუდიოს."</string>
<string name="screenrecord_option_entire_screen" msgid="1732437834603426934">"მთელი ეკრანის ჩაწერა"</string>
<string name="screenrecord_option_single_app" msgid="5954863081500035825">"ერთი აპის ჩაწერა"</string>
- <string name="screenrecord_warning_entire_screen" msgid="8141407178104195610">"სანამ აპის ჩაწერას ახორციელებთ, Android-ს აქვს წვდომა ყველაფერზე, რაც ჩანს თქვენს ეკრანზე ან უკრავს თქვენი მოწყობილობის მეშვეობით. ამიტომ იყავით ფრთხილად პაროლებთან, გადახდის დეტალებთან, შეტყობინებებთან ან სხვა მგრძნობიარე ინფორმაციასთან."</string>
+ <string name="screenrecord_warning_entire_screen" msgid="8141407178104195610">"ჩაწერის განხორციელებისას Android-ს აქვს წვდომა ყველაფერზე, რაც თქვენს ეკრანზე ჩანს ან უკრავს თქვენი მოწყობილობის მეშვეობით. შესაბამისად, გამოიჩინეთ სიფრთხილე პაროლებთან, გადახდის დეტალებთან, შეტყობინებებთან თუ სხვა მგრძნობიარე ინფორმაციასთან დაკავშირებით."</string>
<string name="screenrecord_warning_single_app" msgid="7760723997065948283">"სანამ აპის ჩაწერას ახორციელებთ, Android-ს აქვს წვდომა ყველაფერზე, რაც ჩანს აპში ან ითამაშეთ. ამიტომ იყავით ფრთხილად პაროლებთან, გადახდის დეტალებთან, შეტყობინებებთან ან სხვა მგრძნობიარე ინფორმაციასთან"</string>
<string name="screenrecord_start_recording" msgid="348286842544768740">"ჩაწერის დაწყება"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"აუდიოს ჩაწერა"</string>
@@ -371,11 +373,11 @@
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"ამ ფუნქციის მომწოდებელ სერვისს ექნება წვდომა ყველა ინფორმაციაზე, რომელიც თქვენს ეკრანზე გამოჩნდება ან თქვენს მოწყობილობაზე დაიკვრება ჩაწერის ან ტრანსლირების განმავლობაში. აღნიშნული მოიცავს ისეთ ინფორმაციას, როგორიც არის პაროლები, გადახდის დეტალები, ფოტოები, შეტყობინებები და თქვენ მიერ დაკრული აუდიო."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"დაიწყოს ჩაწერა ან ტრანსლირება?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"დაიწყოს ჩაწერა ან ტრანსლირება <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-ით?"</string>
- <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"გსურთ დართოთ ნება <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> გაზიარების ან ჩაწერისთვის?"</string>
+ <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"გსურთ, დართოთ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-ს გაზიარების ან ჩაწერის ნება?"</string>
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"მთელი ეკრანი"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"ერთი აპი"</string>
<string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"როდესაც თქვენ აზიარებთ, ჩაწერთ ან ტრანსლირებთ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> აქვს წვდომა ყველაფერზე, რაც ჩანს თქვენს ეკრანზე ან უკრავს თქვენს მოწყობილობაზე. ამიტომ იყავით ფრთხილად პაროლებთან, გადახდის დეტალებთან, შეტყობინებებთან ან სხვა მგრძნობიარე ინფორმაციასთან."</string>
- <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"აპის გაზიარებისას, ჩაწერისას ან ტრანსლირებისას <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> აქვს წვდომა აქვს ყველაფერზე, რაც ჩანს აპში ან ითამაშეთ. ამიტომ იყავით ფრთხილად პაროლებთან, გადახდის დეტალებთან, შეტყობინებებთან ან სხვა მგრძნობიარე ინფორმაციასთან."</string>
+ <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"აპის გაზიარებისას, ჩაწერისას ან ტრანსლირებისას <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-ს აქვს წვდომა ყველაფერზე, რაც ამ აპში ჩანს და მასშია გაშვებული. შესაბამისად, გამოიჩინეთ სიფრთხილე პაროლებთან, გადახდის დეტალებთან, შეტყობინებებთან თუ სხვა მგრძნობიარე ინფორმაციასთან დაკავშირებით."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"გაგრძელება"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"გააზიარეთ ან ჩაწერეთ აპი"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"გსურთ ამ აპისთვის გაზიარების ან ჩაწერის უფლების მიცემა?"</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"დაუკარით <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g>-დან"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"მოქმედების გაუქმება"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"მიიტანეთ უფრო ახლოს, რომ დაუკრათ <xliff:g id="DEVICENAME">%1$s</xliff:g>-ზე"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"მიუახლოვდით <xliff:g id="DEVICENAME">%1$s</xliff:g>-ს მისი მეშვეობით დასაკრავად"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"მიმდინარეობს დაკვრა <xliff:g id="DEVICENAME">%1$s</xliff:g>-ზე"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"რაღაც შეცდომა მოხდა. ცადეთ ხელახლა."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"იტვირთება"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ტაბლეტი"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"არააქტიურია, გადაამოწმეთ აპი"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ვერ მოიძებნა"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"კონტროლი მიუწვდომელია"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ხმა"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"დინამიკები და დისპლეები"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"შემოთავაზებული მოწყობილობები"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ტრანსლირების მუშაობის პრინციპი"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ტრანსლაცია"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"თქვენთან ახლოს მყოფ ხალხს თავსებადი Bluetooth მოწყობილობით შეუძლიათ თქვენ მიერ ტრანსლირებული მედიის მოსმენა"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ეს ეკრანი გამოირთვება"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"დასაკეცი მოწყობილობა იხსნება"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"დასაკეცი მოწყობილობა ტრიალებს"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"სტილუსის ბატარეა დაცლის პირასაა"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index bb24d6af3c0a..1e96422b7946 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Төменгі шектік сызық: <xliff:g id="PERCENT">%1$d</xliff:g> пайыз"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Сол жақ шектік сызық: <xliff:g id="PERCENT">%1$d</xliff:g> пайыз"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Оң жақ шектік сызық: <xliff:g id="PERCENT">%1$d</xliff:g> пайыз"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Экран жазғыш"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Экран жазғыш бейнесін өңдеу"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Экранды бейнеге жазудың ағымдағы хабарландыруы"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> қолданбасында \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" әнін ойнату"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Қайтару"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> құрылғысында музыка ойнату үшін оған жақындаңыз."</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Осы жерде ойнау үшін <xliff:g id="DEVICENAME">%1$s</xliff:g> құрылғысына жақындаңыз"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> құрылғысында ойнатылуда."</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Бірдеңе дұрыс болмады. Қайталап көріңіз."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Жүктеліп жатыр"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Өшірулі. Қолданба тексеріңіз."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Табылмады"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Дыбыс деңгейі"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Динамиктер мен дисплейлер"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Тарату қалай жүзеге асады"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Тарату"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Үйлесімді Bluetooth құрылғылары бар маңайдағы адамдар сіз таратып жатқан медиамазмұнды тыңдай алады."</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Бұл экран өшіріледі."</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Бүктемелі құрылғы ашылып жатыр."</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Бүктемелі құрылғы аударылып жатыр."</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Стилус батареясының заряды аз"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 65953058322d..25598777ed21 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"បន្ទាត់បែងចែក​ខាងក្រោម <xliff:g id="PERCENT">%1$d</xliff:g> ភាគរយ"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"បន្ទាត់បែងចែក​ខាងឆ្វេង <xliff:g id="PERCENT">%1$d</xliff:g> ភាគរយ"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"បន្ទាត់បែងចែក​ខាងស្ដាំ <xliff:g id="PERCENT">%1$d</xliff:g> ភាគរយ"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"មុខងារថត​វីដេអូអេក្រង់"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"កំពុង​ដំណើរការ​ការថតអេក្រង់"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ការជូនដំណឹង​ដែល​កំពុង​ដំណើរការ​សម្រាប់​រយៈពេលប្រើ​ការថត​សកម្មភាព​អេក្រង់"</string>
@@ -371,7 +375,7 @@
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"សេវាកម្មដែល​ផ្ដល់​មុខងារ​នេះ​នឹងមាន​សិទ្ធិ​ចូលប្រើ​ព័ត៌មាន​ទាំងអស់​ដែល​អាច​មើលឃើញ​នៅលើ​អេក្រង់​របស់អ្នក ឬ​ដែលចាក់​ពីឧបករណ៍​របស់អ្នក នៅពេល​កំពុង​ថត ឬភ្ជាប់។ ព័ត៌មាន​នេះមាន​ដូចជា ពាក្យសម្ងាត់ ព័ត៌មាន​លម្អិត​អំពីការទូទាត់​ប្រាក់ រូបថត សារ និង​សំឡេង​ដែល​អ្នកចាក់​ជាដើម។"</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ចាប់ផ្ដើម​ថត ឬភ្ជាប់​មែនទេ?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"ចាប់ផ្ដើម​ថត ឬភ្ជាប់​ដោយប្រើ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ឬ?"</string>
- <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"អនុញ្ញាតឱ្យ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ចែករំលែក ឬថតទេ?"</string>
+ <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"អនុញ្ញាតឱ្យ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ចែករំលែក ឬថត?"</string>
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"អេក្រង់ទាំងមូល"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"កម្មវិធីតែមួយ"</string>
<string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"នៅពេលអ្នកកំពុងចែករំលែក ថត ឬបញ្ជូន <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> មានសិទ្ធិចូលប្រើប្រាស់អ្វីៗដែលបង្ហាញឱ្យឃើញនៅលើអេក្រង់របស់អ្នក ឬលេងនៅលើឧបករណ៍របស់អ្នក។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះពាក្យសម្ងាត់ ព័ត៌មាន​លម្អិតអំពី​ការ​ទូទាត់ប្រាក់ សារ ឬព័ត៌មានរសើបផ្សេងទៀត។"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ចាក់ <xliff:g id="SONG_NAME">%1$s</xliff:g> ពី <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ត្រឡប់វិញ"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"រំកិលឱ្យកាន់តែជិត ដើម្បីចាក់នៅលើ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"រំកិលឱ្យកាន់តែជិត <xliff:g id="DEVICENAME">%1$s</xliff:g> ដើម្បីចាក់នៅទីនេះ"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"កំពុង​ចាក់​​នៅ​លើ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"មានអ្វីមួយខុសប្រក្រតី។ សូមព្យាយាមម្ដងទៀត។"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"កំពុងផ្ទុក"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"អសកម្ម ពិនិត្យមើល​កម្មវិធី"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"រកមិន​ឃើញទេ"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"កម្រិតសំឡេង"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ឧបករណ៍បំពងសំឡេង និងផ្ទាំងអេក្រង់"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"របៀបដែលការផ្សាយដំណើរការ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ការ​ផ្សាយ"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"មនុស្សនៅជិត​អ្នកដែលមាន​ឧបករណ៍ប៊្លូធូស​ត្រូវគ្នា​អាចស្តាប់​មេឌៀ​ដែលអ្នកកំពុងផ្សាយបាន"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ អេក្រង់នេះនឹងបិទ"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ឧបករណ៍អាច​បត់បានកំពុងត្រូវបានលា"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ឧបករណ៍អាច​បត់បានកំពុងត្រូវបានលា"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"ថ្មប៊ិកនៅសល់តិច"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 81a37f31aa8f..12d96c342647 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"ಕೆಳಗಿನ ಬೌಂಡರಿ ಶೇಕಡಾ <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ಎಡಭಾಗದ ಬೌಂಡರಿ ಶೇಕಡಾ <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"ಬಲಭಾಗದ ಬೌಂಡರಿ ಶೇಕಡಾ <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡರ್"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಆಗುತ್ತಿದೆ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಸೆಶನ್‌ಗಾಗಿ ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಅಧಿಸೂಚನೆ"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ಹಾಡನ್ನು <xliff:g id="APP_LABEL">%2$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಮಾಡಿ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ರದ್ದುಗೊಳಿಸಿ"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲು ಅದರ ಹತ್ತಿರಕ್ಕೆ ಸರಿಯಿರಿ"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"ಇಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲು <xliff:g id="DEVICENAME">%1$s</xliff:g> ಸಮೀಪಕ್ಕೆ ಹೋಗಿ"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಆಗುತ್ತಿದೆ"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"ಏನೋ ತಪ್ಪಾಗಿದೆ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"ಲೋಡ್ ಆಗುತ್ತಿದೆ"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"ನಿಷ್ಕ್ರಿಯ, ಆ್ಯಪ್ ಪರಿಶೀಲಿಸಿ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ಕಂಡುಬಂದಿಲ್ಲ"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ವಾಲ್ಯೂಮ್"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ಸ್ಪೀಕರ್‌ಗಳು ಮತ್ತು ಡಿಸ್‌ಪ್ಲೇಗಳು"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ಪ್ರಸಾರವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ಪ್ರಸಾರ"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ಹೊಂದಾಣಿಕೆಯಾಗುವ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳನ್ನು ಹೊಂದಿರುವ ಸಮೀಪದಲ್ಲಿರುವ ಜನರು ನೀವು ಪ್ರಸಾರ ಮಾಡುತ್ತಿರುವ ಮಾಧ್ಯಮವನ್ನು ಆಲಿಸಬಹುದು"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ಈ ಸ್ಕ್ರೀನ್ ಆಫ್ ಆಗುತ್ತದೆ"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ಫೋಲ್ಡ್ ಮಾಡಬಹುದಾದ ಸಾಧನವನ್ನು ಅನ್‌ಫೋಲ್ಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ಫೋಲ್ಡ್ ಮಾಡಬಹುದಾದ ಸಾಧನವನ್ನು ಸುತ್ತಲೂ ತಿರುಗಿಸಲಾಗುತ್ತಿದೆ"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"ಸ್ಟೈಲಸ್ ಬ್ಯಾಟರಿ ಕಡಿಮೆಯಿದೆ"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 1570a6aa2370..e42e10ef753a 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"하단 가장자리 <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"왼쪽 가장자리 <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"오른쪽 가장자리 <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"화면 녹화"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"화면 녹화 처리 중"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"화면 녹화 세션에 관한 지속적인 알림"</string>
@@ -371,11 +375,11 @@
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"이 기능을 제공하는 서비스는 녹화 또는 전송 중에 화면에 표시되거나 기기에서 재생되는 모든 정보에 액세스할 수 있습니다. 여기에는 비밀번호, 결제 세부정보, 사진, 메시지, 재생하는 오디오 같은 정보가 포함됩니다."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"녹화 또는 전송을 시작하시겠습니까?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>으로 녹화 또는 전송을 시작하시겠습니까?"</string>
- <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에서 공유 또는 녹화를 허용할까요?"</string>
+ <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에서 공유 또는 녹화하도록 허용할까요?"</string>
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"전체 화면"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"단일 앱"</string>
<string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"공유하거나 녹화하거나 전송할 때 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 앱에서 화면에 표시되거나 기기에서 재생되는 모든 항목에 액세스할 수 있습니다. 따라서 비밀번호, 결제 세부정보, 메시지 등 민감한 정보가 노출되지 않도록 주의하세요."</string>
- <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"앱을 공유하거나 녹화하거나 전송할 때는 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에서 해당 앱에 표시되거나 재생되는 모든 항목에 액세스할 수 있으므로 비밀번호, 결제 세부정보, 메시지 등 민감한 정보가 노출되지 않도록 주의하세요."</string>
+ <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"앱을 공유하거나 녹화하거나 전송할 때는 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에서 해당 앱에 표시되거나 앱에서 재생되는 모든 항목에 액세스할 수 있으므로 비밀번호, 결제 세부정보, 메시지 등 민감한 정보가 노출되지 않도록 주의하세요."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"계속"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"앱 공유 또는 녹화"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"앱에서 공유하거나 기록하도록 허용하시겠습니까?"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>에서 <xliff:g id="SONG_NAME">%1$s</xliff:g> 재생"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"실행취소"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>에서 재생하려면 기기를 더 가까이로 옮기세요."</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"현재 기기에서 재생하려면 <xliff:g id="DEVICENAME">%1$s</xliff:g>에 더 가까이 이동합니다."</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g>에서 재생 중"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"문제가 발생했습니다. 다시 시도해 주세요."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"로드 중"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"비활성. 앱을 확인하세요."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"찾을 수 없음"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"볼륨"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"스피커 및 디스플레이"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"브로드캐스팅 작동 원리"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"브로드캐스트"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"호환되는 블루투스 기기를 가진 근처의 사용자가 내가 브로드캐스트 중인 미디어를 수신 대기할 수 있습니다."</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ 이 화면이 꺼집니다."</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"폴더블 기기를 펼치는 모습"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"폴더블 기기를 뒤집는 모습"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"스타일러스 배터리 부족"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index b1206840b722..ec9e96200816 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Ылдый жагы <xliff:g id="PERCENT">%1$d</xliff:g> пайызга"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Сол жагы <xliff:g id="PERCENT">%1$d</xliff:g> пайызга"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Оң жагы <xliff:g id="PERCENT">%1$d</xliff:g> пайызга"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"экрандан видео жаздырып алуу"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Экрандан жаздырылып алынган видео иштетилүүдө"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Экранды жаздыруу сеансы боюнча учурдагы билдирме"</string>
@@ -371,16 +375,16 @@
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Жаздырып же тышкы экранга чыгарып жатканда, бул колдонмо экраныңыздагы бардык маалыматты же түзмөктө ойнолуп жаткан бардык нерселерди (сырсөздөрдү, төлөмдүн чоо-жайын, сүрөттөрдү, билдирүүлөрдү жана угуп жаткан аудиофайлдарды) көрө алат."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Жаздырып же тышкы экранга чыгарып баштайсызбы?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосу аркылуу жаздырып же тышкы экранга чыгарып баштайсызбы?"</string>
- <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосуна бөлүшүүгө же жаздырууга уруксат бересизби?"</string>
+ <string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосуна экранды бөлүшүүгө же андан видео тартууга уруксат бересизби?"</string>
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"Бүтүндөй экран"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"Жалгыз колдонмо"</string>
- <string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"Бөлүшүп, жаздырып же тышкы экранда бөлүшкөндө <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> экраныңызда көрүнүп жана түзмөктө ойнотулуп жаткан нерселерге мүмкүнчүлүк алат. Андыктан сырсөздөрдү, төлөм маалыматын, билдирүүлөрдү жана башка купуя маалыматты көрсөтүп албаңыз."</string>
- <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Бөлүшүп, жаздырып же тышкы экранда бөлүшкөндө <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ал колдонмодо көрүнүп жана ойнотулуп жаткан нерселерге мүмкүнчүлүк алат. Андыктан сырсөздөрдү, төлөм маалыматын, билдирүүлөрдү жана башка купуя маалыматты көрсөтүп албаңыз."</string>
+ <string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"Экранды көрсөтүп, тышка чыгарып же андан видео тартып жатканда, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> түзмөктүн экранындагы нерселердин баарын көрө алат. Андыктан сырсөздөр, төлөм маалыматы, билдирүүлөр сыяктуу купуя маалыматты киргизүүдө же көрүүдө этият болуңуз."</string>
+ <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Экранды көрсөтүп, тышка чыгарып же андан видео тартып жатканда, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> түзмөктүн экранындагы нерселердин баарын көрө алат. Андыктан сырсөздөр, төлөм маалыматы, билдирүүлөр сыяктуу купуя маалыматты киргизүүдө же көрүүдө этият болуңуз."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Улантуу"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Колдонмону бөлүшүү же жаздыруу"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"Бул колдонмого бөлүшүп же жаздырууга уруксат бересизби?"</string>
- <string name="media_projection_permission_dialog_system_service_warning_entire_screen" msgid="8801616203805837575">"Бөлүшүп, жаздырып же тышкы экранга чыгарганда бул колдонмо экраныңызда көрүнүп жана түзмөктө ойнотулуп жаткан нерселерге мүмкүнчүлүк алат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү жана башка купуя маалыматты көрсөтүп албаңыз."</string>
- <string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"Бөлүшүп, жаздырып же тышкы экранга чыгарганда бул колдонмо ал колдонмодо көрсөтүлүп жана ойнотулуп жаткан нерселерге мүмкүнчүлүк алат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү жана башка купуя маалыматты көрсөтүп албаңыз."</string>
+ <string name="media_projection_permission_dialog_system_service_warning_entire_screen" msgid="8801616203805837575">"Экранды көрсөтүп, тышка чыгарып же андан видео тартып жатканда, бул колдонмо түзмөктүн экранындагы нерселердин баарын көрө алат. Андыктан сырсөздөр, төлөм маалыматы, билдирүүлөр сыяктуу купуя маалыматты киргизүүдө же көрүүдө этият болуңуз."</string>
+ <string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"Экранды көрсөтүп, тышка чыгарып же андан видео тартып жатканда, бул колдонмо түзмөктүн экранындагы нерселердин баарын көрө алат. Андыктан сырсөздөр, төлөм маалыматы, билдирүүлөр сыяктуу купуя маалыматты киргизүүдө же көрүүдө этият болуңуз."</string>
<string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"IT администраторуңуз бөгөттөп койгон"</string>
<string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"Түзмөк саясаты экрандагыны тартып алууну өчүрүп койгон"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Баарын тазалап салуу"</string>
@@ -745,7 +749,7 @@
<string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Жеткиликтүү болгондо мобилдик Интернет автоматтык түрдө которулбайт"</string>
<string name="auto_data_switch_dialog_negative_button" msgid="2370876875999891444">"Жок, рахмат"</string>
<string name="auto_data_switch_dialog_positive_button" msgid="8531782041263087564">"Ооба, которулуу"</string>
- <string name="touch_filtered_warning" msgid="8119511393338714836">"Уруксат берүү сурамыңыз көрүнбөй калгандыктан, Жөндөөлөр жообуңузду ырастай албай жатат."</string>
+ <string name="touch_filtered_warning" msgid="8119511393338714836">"Уруксат берүү сурамыңыз көрүнбөй калгандыктан, Параметрлер жообуңузду ырастай албай жатат."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g> колдонмосуна <xliff:g id="APP_2">%2$s</xliff:g> үлгүлөрүн көрсөтүүгө уруксат берилсинби?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- <xliff:g id="APP">%1$s</xliff:g> колдонмосунун маалыматын окуйт"</string>
<string name="slice_permission_text_2" msgid="6758906940360746983">"- <xliff:g id="APP">%1$s</xliff:g> колдонмосунда аракеттерди аткарат"</string>
@@ -836,7 +840,7 @@
<string name="controls_media_active_session" msgid="3146882316024153337">"Учурдагы медиа сеансын жашыруу мүмкүн эмес."</string>
<string name="controls_media_dismiss_button" msgid="4485675693008031646">"Жашыруу"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Улантуу"</string>
- <string name="controls_media_settings_button" msgid="5815790345117172504">"Жөндөөлөр"</string>
+ <string name="controls_media_settings_button" msgid="5815790345117172504">"Параметрлер"</string>
<string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ыры (аткаруучу: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) <xliff:g id="APP_LABEL">%3$s</xliff:g> колдонмосунан ойнотулуп жатат"</string>
<string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g> ичинен <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
<string name="controls_media_button_play" msgid="2705068099607410633">"Ойнотуу"</string>
@@ -849,11 +853,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ырын (аткаруучу: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) <xliff:g id="APP_LABEL">%3$s</xliff:g> колдонмосунан ойнотуу"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ырын <xliff:g id="APP_LABEL">%2$s</xliff:g> колдонмосунан ойнотуу"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Кайтаруу"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> түзмөгүндө ойнотуу үчүн жакыныраак жылдырыңыз"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Бул жерде ойнотуу үчүн <xliff:g id="DEVICENAME">%1$s</xliff:g> түзмөгүнө жакындатыңыз"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> түзмөгүндө ойнотуу үчүн жакындатыңыз"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> аркылуу ойнотулууда"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Бир жерден ката кетти. Кайра аракет кылыңыз."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Жүктөлүүдө"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Жигерсиз. Колдонмону текшериңиз"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Табылган жок"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Үндүн катуулугу"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Динамиктер жана дисплейлер"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Кабарлоо кантип иштейт"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Кабарлоо"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Шайкеш Bluetooth түзмөктөрү болгон жакын жердеги кишилер кабарлап жаткан медиаңызды уга алышат"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Бул экран өчөт"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Ачылып турган бүктөлмө түзмөк"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Оодарылып жаткан бүктөлмө түзмөк"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Стилустун батареясы отурайын деп калды"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 49ef330dcc52..fff25448b2e4 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -40,6 +40,10 @@
<dimen name="biometric_dialog_button_negative_max_width">140dp</dimen>
<dimen name="biometric_dialog_button_positive_max_width">116dp</dimen>
+ <!-- Lock pattern view size, align sysui biometric_auth_pattern_view_size -->
+ <dimen name="biometric_auth_pattern_view_size">248dp</dimen>
+ <dimen name="biometric_auth_pattern_view_max_size">348dp</dimen>
+
<dimen name="global_actions_power_dialog_item_height">130dp</dimen>
<dimen name="global_actions_power_dialog_item_bottom_margin">35dp</dimen>
diff --git a/packages/SystemUI/res/values-land/styles.xml b/packages/SystemUI/res/values-land/styles.xml
index aefd9981d02e..a0e721e571d8 100644
--- a/packages/SystemUI/res/values-land/styles.xml
+++ b/packages/SystemUI/res/values-land/styles.xml
@@ -29,11 +29,11 @@
<style name="AuthCredentialPatternContainerStyle">
<item name="android:gravity">center</item>
- <item name="android:maxHeight">320dp</item>
- <item name="android:maxWidth">320dp</item>
- <item name="android:minHeight">200dp</item>
- <item name="android:minWidth">200dp</item>
- <item name="android:paddingHorizontal">60dp</item>
+ <item name="android:maxHeight">@dimen/biometric_auth_pattern_view_max_size</item>
+ <item name="android:maxWidth">@dimen/biometric_auth_pattern_view_max_size</item>
+ <item name="android:minHeight">@dimen/biometric_auth_pattern_view_size</item>
+ <item name="android:minWidth">@dimen/biometric_auth_pattern_view_size</item>
+ <item name="android:paddingHorizontal">32dp</item>
<item name="android:paddingVertical">20dp</item>
</style>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 446deb05cf00..0b8a9de5e1cc 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"ຂອບເຂດທາງລຸ່ມ <xliff:g id="PERCENT">%1$d</xliff:g> ເປີເຊັນ"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ຂອບເຂດທາງຊ້າຍ <xliff:g id="PERCENT">%1$d</xliff:g> ເປີເຊັນ"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"ຂອບເຂດທາງຂວາ <xliff:g id="PERCENT">%1$d</xliff:g> ເປີເຊັນ"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"ໂປຣແກຣມບັນທຶກໜ້າຈໍ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ກຳລັງປະມວນຜົນການບັນທຶກໜ້າຈໍ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ການແຈ້ງເຕືອນສຳລັບເຊດຊັນການບັນທຶກໜ້າຈໍໃດໜຶ່ງ"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ຫຼິ້ນ <xliff:g id="SONG_NAME">%1$s</xliff:g> ຈາກ <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ຍົກເລີກ"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"ຍ້າຍໄປໃກ້ຂຶ້ນເພື່ອຫຼິ້ນຢູ່ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"ກະລຸນາຍ້າຍເຂົ້າໃກ້ <xliff:g id="DEVICENAME">%1$s</xliff:g> ເພື່ອຫຼິ້ນຢູ່ບ່ອນນີ້"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"ກຳລັງຫຼິ້ນຢູ່ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"ມີບາງຢ່າງຜິດພາດເກີດຂຶ້ນ. ກະລຸນາລອງໃໝ່."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"ກຳລັງໂຫຼດ"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"ບໍ່ເຮັດວຽກ, ກະລຸນາກວດສອບແອັບ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ບໍ່ພົບ"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ລະດັບສຽງ"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ລຳໂພງ ແລະ ຈໍສະແດງຜົນ"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ການອອກອາກາດເຮັດວຽກແນວໃດ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ອອກອາກາດ"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ຄົນທີ່ຢູ່ໃກ້ທ່ານທີ່ມີອຸປະກອນ Bluetooth ທີ່ເຂົ້າກັນໄດ້ຈະສາມາດຟັງມີເດຍທີ່ທ່ານກຳລັງອອກອາກາດຢູ່ໄດ້"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ໜ້າຈໍນີ້ຈະປິດ"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ອຸປະກອນທີ່ພັບໄດ້ກຳລັງກາງອອກ"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ອຸປະກອນທີ່ພັກໄດ້ກຳລັງປີ້ນໄປມາ"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"ແບັດເຕີຣີປາກກາເຫຼືອໜ້ອຍ"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index db518c100a0d..901773259470 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Apatinė riba – <xliff:g id="PERCENT">%1$d</xliff:g> proc."</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Kairioji riba – <xliff:g id="PERCENT">%1$d</xliff:g> proc."</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Dešinioji riba – <xliff:g id="PERCENT">%1$d</xliff:g> proc."</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Ekrano vaizdo įrašytuvas"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Apdorojam. ekrano vaizdo įraš."</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Šiuo metu rodomas ekrano įrašymo sesijos pranešimas"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Leisti „<xliff:g id="SONG_NAME">%1$s</xliff:g>“ iš „<xliff:g id="APP_LABEL">%2$s</xliff:g>“"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Anuliuoti"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Prieikite arčiau, kad galėtumėte leisti įrenginyje „<xliff:g id="DEVICENAME">%1$s</xliff:g>“"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Perkelkite arčiau „<xliff:g id="DEVICENAME">%1$s</xliff:g>“, kad būtų galima leisti čia"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Leidžiama įrenginyje „<xliff:g id="DEVICENAME">%1$s</xliff:g>“"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Kažkas ne taip. Bandykite dar kartą."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Įkeliama"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktyvu, patikrinkite progr."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nerasta"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Garsumas"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Garsiakalbiai ir ekranai"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kaip veikia transliacija"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transliacija"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Netoliese esantys žmonės, turintys suderinamus „Bluetooth“ įrenginius, gali klausyti jūsų transliuojamos medijos"</string>
@@ -1015,5 +1023,6 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Šis ekranas išsijungs"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Lankstomasis įrenginys išlankstomas"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Lankstomasis įrenginys apverčiamas"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Senka rašiklio akumuliatorius"</string>
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Liko akumuliatoriaus įkrovos: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Prijunkite rašiklį prie kroviklio"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 5ea8da2889d8..c4cdc06a10c3 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Apakšmala: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Kreisā mala: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Labā mala: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Ekrāna ierakstītājs"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekrāna ieraksta apstrāde"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Aktīvs paziņojums par ekrāna ierakstīšanas sesiju"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Atskaņojiet failu “<xliff:g id="SONG_NAME">%1$s</xliff:g>” no lietotnes <xliff:g id="APP_LABEL">%2$s</xliff:g>."</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Atsaukt"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Pārvietojiet savu ierīci tuvāk, lai atskaņotu mūziku ierīcē “<xliff:g id="DEVICENAME">%1$s</xliff:g>”."</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Pārvietojieties tuvāk ierīcei “<xliff:g id="DEVICENAME">%1$s</xliff:g>”, lai atskaņotu šeit"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Notiek atskaņošana ierīcē <xliff:g id="DEVICENAME">%1$s</xliff:g>."</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Radās kļūda. Mēģiniet vēlreiz."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Notiek ielāde"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktīva, pārbaudiet lietotni"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Netika atrasta"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Skaļums"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Skaļruņi un displeji"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kā darbojas apraide"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Apraide"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Tuvumā esošās personas ar saderīgām Bluetooth ierīcēm var klausīties jūsu apraidīto multivides saturu."</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Šis ekrāns tiks izslēgts."</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Salokāma ierīce tiek atlocīta"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Salokāma ierīce tiek apgriezta"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Zems skārienekrāna pildspalvas akumulatora līmenis"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 0575b102e405..cbdee207e050 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Долна граница <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Лева граница <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Десна граница <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Сликите од екранот во работниот профил се зачувуваат во апликацијата <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Датотеки"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Снимач на екран"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Се обработува снимка од екран"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Тековно известување за сесија за снимање на екранот"</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Пуштете <xliff:g id="SONG_NAME">%1$s</xliff:g> на <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Врати"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Приближете се за да пуштите на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Приближете се до <xliff:g id="DEVICENAME">%1$s</xliff:g> за да пуштите тука"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Пуштено на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Нешто не е во ред. Обидете се повторно."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Се вчитува"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"таблет"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивна, провери апликација"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не е најдено"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Контролата не е достапна"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Јачина на звук"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Звучници и екрани"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Предложени уреди"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Како функционира емитувањето"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Емитување"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Луѓето во ваша близина со компатибилни уреди со Bluetooth може да ги слушаат аудиозаписите што ги емитувате"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Екранов ќе се исклучи"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Преклопувачки уред се отклопува"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Преклопувачки уред се врти"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Слаба батерија на пенкало"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 9a2353c6b8ad..ac5c0747a47b 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"താഴെയുള്ള അതിർത്തി <xliff:g id="PERCENT">%1$d</xliff:g> ശതമാനം"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ഇടത് വശത്തെ അതിർത്തി <xliff:g id="PERCENT">%1$d</xliff:g> ശതമാനം"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"വലത് വശത്തെ അതിർത്തി <xliff:g id="PERCENT">%1$d</xliff:g> ശതമാനം"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"ഔദ്യോഗിക പ്രൊഫൈലിന്റെ സ്ക്രീന്‍ഷോട്ടുകൾ <xliff:g id="APP">%1$s</xliff:g> ആപ്പിൽ സംരക്ഷിച്ചു"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ഫയലുകൾ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"സ്ക്രീൻ റെക്കോർഡർ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"സ്ക്രീൻ റെക്കോർഡിംഗ് പ്രോസസുചെയ്യുന്നു"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ഒരു സ്ക്രീൻ റെക്കോർഡിംഗ് സെഷനായി നിലവിലുള്ള അറിയിപ്പ്"</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> എന്ന ഗാനം <xliff:g id="APP_LABEL">%2$s</xliff:g> ആപ്പിൽ പ്ലേ ചെയ്യുക"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"പഴയപടിയാക്കുക"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> എന്നതിൽ പ്ലേ ചെയ്യാൻ അടുത്തേക്ക് നീക്കുക"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"ഇവിടെ പ്ലേ ചെയ്യാൻ <xliff:g id="DEVICENAME">%1$s</xliff:g> എന്നതിന് അടുത്തേക്ക് നീക്കുക"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> എന്നതിൽ പ്ലേ ചെയ്യുന്നു"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"എന്തോ കുഴപ്പമുണ്ടായി. വീണ്ടും ശ്രമിക്കുക."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"ലോഡ് ചെയ്യുന്നു"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ടാബ്‌ലെറ്റ്"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"നിഷ്‌ക്രിയം, ആപ്പ് പരിശോധിക്കൂ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"കണ്ടെത്തിയില്ല"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"നിയന്ത്രണം ലഭ്യമല്ല"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"വോളിയം"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"സ്‌പീക്കറുകളും ഡിസ്പ്ലേകളും"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"നിർദ്ദേശിച്ച ഉപകരണങ്ങൾ"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ബ്രോഡ്‌കാസ്‌റ്റ് എങ്ങനെയാണ് പ്രവർത്തിക്കുന്നത്"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ബ്രോഡ്‌കാസ്റ്റ്"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"അനുയോജ്യമായ Bluetooth ഉപകരണങ്ങളോടെ സമീപമുള്ള ആളുകൾക്ക് നിങ്ങൾ ബ്രോഡ്‌കാസ്‌റ്റ് ചെയ്യുന്ന മീഡിയ കേൾക്കാനാകും"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ഈ സ്ക്രീൻ ഓഫാകും"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ഫോൾഡ് ചെയ്യാവുന്ന ഉപകരണം അൺഫോൾഡ് ആകുന്നു"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ഫോൾഡ് ചെയ്യാവുന്ന ഉപകരണം, കറങ്ങുന്ന വിധത്തിൽ ഫ്ലിപ്പ് ആകുന്നു"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"സ്റ്റൈലസിന്റെ ബാറ്ററി ചാർജ് കുറവാണ്"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 08c47b0f4df4..cb77336df77e 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Доод талын хязгаар <xliff:g id="PERCENT">%1$d</xliff:g> хувь"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Зүүн талын хязгаар <xliff:g id="PERCENT">%1$d</xliff:g> хувь"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Баруун талын хязгаар <xliff:g id="PERCENT">%1$d</xliff:g> хувь"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Дэлгэцийн үйлдэл бичигч"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Дэлгэц бичлэг боловсруулж байна"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Дэлгэц бичих горимын үргэлжилж буй мэдэгдэл"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g>-г <xliff:g id="APP_LABEL">%2$s</xliff:g> дээр тоглуулах"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Болих"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> дээр тоглуулахын тулд төхөөрөмжөө ойртуулна уу"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Энд тоглуулахын тулд <xliff:g id="DEVICENAME">%1$s</xliff:g>-д ойртоно уу"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> дээр тоглуулж байна"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Алдаа гарлаа. Дахин оролдоно уу."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Ачаалж байна"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Идэвхгүй байна, аппыг шалгана уу"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Олдсонгүй"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Дууны түвшин"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Чанга яригч ба дэлгэц"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Нэвтрүүлэлт хэрхэн ажилладаг вэ?"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Нэвтрүүлэлт"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Тохиромжтой Bluetooth төхөөрөмжүүдтэй таны ойролцоох хүмүүс таны нэвтрүүлж буй медиаг сонсох боломжтой"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Энэ дэлгэц унтарна"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Эвхэгддэг төхөөрөмжийг дэлгэж байна"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Эвхэгддэг төхөөрөмжийг хөнтөрч байна"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Мэдрэгч үзэгний батарей бага байна"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 103e8d204772..4f0b83fc63ae 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"खालील सीमेपासून <xliff:g id="PERCENT">%1$d</xliff:g> टक्के"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"डाव्या सीमेपासून <xliff:g id="PERCENT">%1$d</xliff:g> टक्के"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"उजव्या सीमेपासून <xliff:g id="PERCENT">%1$d</xliff:g> टक्के"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रेकॉर्डर"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रीन रेकॉर्डिंग प्रोसेस सुरू"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"स्क्रीन रेकॉर्ड सत्रासाठी सुरू असलेली सूचना"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> मध्ये <xliff:g id="SONG_NAME">%1$s</xliff:g> प्ले करा"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"पहिल्यासारखे करा"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> वर प्ले करण्यासाठी जवळ जा"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"येथे प्ले करण्यासाठी <xliff:g id="DEVICENAME">%1$s</xliff:g> च्या जवळ जा"</string>
- <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> वर प्ले केला जात आहे"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
+ <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> वर प्ले होत आहे"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"काहीतरी चूक झाली. पुन्हा प्रयत्न करा."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"लोड करत आहे"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय, ॲप तपासा"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"आढळले नाही"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"व्हॉल्यूम"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"स्पीकर आणि डिस्प्ले"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ब्रॉडकास्टिंग कसे काम करते"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ब्रॉडकास्ट करा"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"कंपॅटिबल ब्लूटूथ डिव्‍हाइस असलेले तुमच्या जवळपासचे लोक हे तुम्ही ब्रॉडकास्ट करत असलेला मीडिया ऐकू शकतात"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ही स्क्रीन बंद होईल"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"फोल्ड करता येण्यासारखे डिव्हाइस अनफोल्ड केले जात आहे"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"फोल्ड करता येण्यासारखे डिव्हाइस आजूबाजूला फ्लिप केले जात आहे"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"स्टायलस बॅटरी कमी आहे"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index b0b40962123f..f08f6a654658 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Sempadan bawah <xliff:g id="PERCENT">%1$d</xliff:g> peratus"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Sempadan kiri <xliff:g id="PERCENT">%1$d</xliff:g> peratus"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Sempadan kanan <xliff:g id="PERCENT">%1$d</xliff:g> peratus"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Perakam Skrin"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses rakaman skrin"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pemberitahuan breterusan untuk sesi rakaman skrin"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Mainkan <xliff:g id="SONG_NAME">%1$s</xliff:g> daripada <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Buat asal"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Alihkan lebih dekat untuk bermain pada<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Dekatkan dengan <xliff:g id="DEVICENAME">%1$s</xliff:g> untuk bermain di sini"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Dimainkan pada <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Kesilapan telah berlaku. Cuba lagi."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Memuatkan"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Tidak aktif, semak apl"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Tidak ditemukan"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Kelantangan"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Pembesar Suara &amp; Paparan"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cara siaran berfungsi"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Siarkan"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Orang berdekatan anda dengan peranti Bluetooth yang serasi boleh mendengar media yang sedang anda siarkan"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Skrin ini akan dimatikan"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Peranti boleh lipat dibuka"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Peranti boleh lipat diterbalikkan"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Bateri stilus lemah"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 2014f5c566c5..9b6e95cbcd53 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"အောက်ခြေအနားသတ် <xliff:g id="PERCENT">%1$d</xliff:g> ရာခိုင်နှုန်း"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ဘယ်ဘက်အနားသတ် <xliff:g id="PERCENT">%1$d</xliff:g> ရာခိုင်နှုန်း"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"ညာဘက်အနားသတ် <xliff:g id="PERCENT">%1$d</xliff:g> ရာခိုင်နှုန်း"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"ဖန်သားပြင် ရိုက်ကူးမှု"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"စကရင်ရိုက်ကူးမှု အပြီးသတ်နေသည်"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ဖန်သားပြင် ရိုက်ကူးသည့် စက်ရှင်အတွက် ဆက်တိုက်လာနေသော အကြောင်းကြားချက်"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ကို <xliff:g id="APP_LABEL">%2$s</xliff:g> တွင် ဖွင့်ပါ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"နောက်ပြန်ရန်"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> တွင်ဖွင့်ရန် အနီးသို့ရွှေ့ပါ"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"ဤနေရာတွင်ဖွင့်ရန် <xliff:g id="DEVICENAME">%1$s</xliff:g> အနီးသို့တိုးပါ"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> တွင် ဖွင့်နေသည်"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"တစ်ခုခုမှားသွားသည်။ ထပ်စမ်းကြည့်ပါ။"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"ဖွင့်နေသည်"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"ရပ်နေသည်၊ အက်ပ်ကို စစ်ဆေးပါ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"မတွေ့ပါ"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"အသံအတိုးအကျယ်"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"စပီကာနှင့် ဖန်သားပြင်များ"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ထုတ်လွှင့်မှုဆောင်ရွက်ပုံ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ထုတ်လွှင့်ခြင်း"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"အနီးရှိတွဲသုံးနိုင်သော ဘလူးတုသ်သုံးစက် အသုံးပြုသူများက သင်ထုတ်လွှင့်နေသော မီဒီယာကို နားဆင်နိုင်သည်"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ဤဖန်သားပြင်ကို ပိတ်လိုက်မည်"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ခေါက်နိုင်သောစက်ကို ဖြန့်လိုက်သည်"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ခေါက်နိုင်သောစက်ကို တစ်ဘက်သို့ လှန်လိုက်သည်"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"စတိုင်လပ်စ် ဘက်ထရီ အားနည်းနေသည်"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 2b1ebfdb4f27..7cc0a1a9a1ff 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Nedre grense <xliff:g id="PERCENT">%1$d</xliff:g> prosent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Venstre grense <xliff:g id="PERCENT">%1$d</xliff:g> prosent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Høyre grense <xliff:g id="PERCENT">%1$d</xliff:g> prosent"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Skjermopptaker"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skjermopptaket"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Vedvarende varsel for et skjermopptak"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spill av <xliff:g id="SONG_NAME">%1$s</xliff:g> fra <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Angre"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Flytt nærmere for å spille av på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Flytt deg nærmere <xliff:g id="DEVICENAME">%1$s</xliff:g> for å spille av her"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Spilles av på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Noe gikk galt. Prøv på nytt."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Laster inn"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Sjekk appen"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ikke funnet"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volum"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Høyttalere og skjermer"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Slik fungerer kringkasting"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Kringkasting"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Folk i nærheten med kompatible Bluetooth-enheter kan lytte til mediene du kringkaster"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Denne skjermen slås av"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"En sammenleggbar enhet blir brettet ut"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"En sammenleggbar enhet blir snudd"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Det er lite batteri i pekepennen"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 98fe304a06b4..f8deac686a94 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"फेदबाट <xliff:g id="PERCENT">%1$d</xliff:g> प्रतिशत"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"बायाँ किनाराबाट <xliff:g id="PERCENT">%1$d</xliff:g> प्रतिशत"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"दायाँ किनाराबाट <xliff:g id="PERCENT">%1$d</xliff:g> प्रतिशत"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रिन रेकर्डर"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रिन रेकर्डिङको प्रक्रिया अघि बढाइँदै"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"कुनै स्क्रिन रेकर्ड गर्ने सत्रका लागि चलिरहेको सूचना"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> बोलको गीत <xliff:g id="APP_LABEL">%2$s</xliff:g> मा बजाउनुहोस्"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"अन्डू गर्नुहोस्"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> मा प्ले गर्न आफ्नो डिभाइस नजिकै लैजानुहोस्"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"तपाईं यहाँ प्ले गर्न चाहनुहुन्छ भने आफ्नो डिभाइसलाई <xliff:g id="DEVICENAME">%1$s</xliff:g> नजिकै लैजानुहोस्"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> मा प्ले गरिँदै छ"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"केही चिज गडबड भयो। फेरि प्रयास गर्नुहोस्।"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"लोड हुँदै छ"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय छ, एप जाँच गर्नु…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"फेला परेन"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"भोल्युम"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"स्पिकर तथा डिस्प्लेहरू"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"प्रसारण गर्ने सुविधाले कसरी काम गर्छ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"प्रसारण"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"कम्प्याटिबल ब्लुटुथ डिभाइस भएका नजिकैका मान्छेहरू तपाईंले प्रसारण गरिरहनुभएको मिडिया सुन्न सक्छन्"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ यो स्क्रिन अफ हुने छ"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"फोल्ड गर्न मिल्ने डिभाइस अनफोल्ड गरेको देखाइएको एनिमेसन"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"फोल्ड गर्न मिल्ने डिभाइस यताउता पल्टाएर देखाइएको एनिमेसन"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलसको ब्याट्री लो छ"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index e49b9fac5ccb..7a0b6d3230fc 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Ondergrens <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Linkergrens <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Rechtergrens <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Schermopname"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Schermopname verwerken"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Doorlopende melding voor een schermopname-sessie"</string>
@@ -849,11 +853,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> van <xliff:g id="ARTIST_NAME">%2$s</xliff:g> afspelen via <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> afspelen via <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Ongedaan maken"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Ga dichter naar <xliff:g id="DEVICENAME">%1$s</xliff:g> toe om af te spelen"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Ga dichter bij <xliff:g id="DEVICENAME">%1$s</xliff:g> staan om hier af te spelen"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Houd dichter bij <xliff:g id="DEVICENAME">%1$s</xliff:g> om af te spelen"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Afspelen op <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Er is iets misgegaan. Probeer het opnieuw."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Laden"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Inactief, check de app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Niet gevonden"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers en schermen"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitzenden werkt"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Uitzending"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Mensen bij jou in de buurt met geschikte bluetooth-apparaten kunnen luisteren naar de media die je uitzendt"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Dit scherm gaat uit"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Opvouwbaar apparaat wordt uitgevouwen"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Opvouwbaar apparaat wordt gedraaid"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Batterij van stylus bijna leeg"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 71d0822d078b..6a3a282fc436 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"ନିମ୍ନ ସୀମାରେଖା <xliff:g id="PERCENT">%1$d</xliff:g> ଶତକଡ଼ା"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ବାମ ସୀମାରେଖା <xliff:g id="PERCENT">%1$d</xliff:g> ଶତକଡ଼ା"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"ଡାହାଣ ସୀମାରେଖା <xliff:g id="PERCENT">%1$d</xliff:g> ଶତକଡ଼ା"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"ସ୍କ୍ରିନ୍ ରେକର୍ଡର୍"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ସ୍କ୍ରିନ ରେକର୍ଡିଂର ପ୍ରକ୍ରିୟାକରଣ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ଏକ ସ୍କ୍ରି‍ନ୍‍ ରେକର୍ଡ୍‍ ସେସନ୍‍ ପାଇଁ ଚାଲୁଥିବା ବିଜ୍ଞପ୍ତି"</string>
@@ -849,11 +853,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g>ରୁ <xliff:g id="ARTIST_NAME">%2$s</xliff:g>ଙ୍କ <xliff:g id="SONG_NAME">%1$s</xliff:g> ଚଲାନ୍ତୁ"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>ରୁ <xliff:g id="SONG_NAME">%1$s</xliff:g> ଚଲାନ୍ତୁ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ପୂର୍ବବତ୍ କରନ୍ତୁ"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ରେ ଚଲାଇବା ପାଇଁ ପାଖକୁ ମୁଭ କରନ୍ତୁ"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"ଏଠାରେ ଚଲାଇବା ପାଇଁ <xliff:g id="DEVICENAME">%1$s</xliff:g>ର ପାଖକୁ ମୁଭ କରନ୍ତୁ"</string>
- <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ରେ ଚାଲୁଛି"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ରେ ପ୍ଲେ କରିବା ପାଇଁ ପାଖକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
+ <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ରେ ପ୍ଲେ ହେଉଛି"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"କିଛି ତ୍ରୁଟି ହୋଇଛି। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"ଲୋଡ ହେଉଛି"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"ନିଷ୍କ୍ରିୟ ଅଛି, ଆପ ଯାଞ୍ଚ କରନ୍ତୁ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ମିଳିଲା ନାହିଁ"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ଭଲ୍ୟୁମ"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ସ୍ପିକର ଏବଂ ଡିସପ୍ଲେଗୁଡ଼ିକ"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ବ୍ରଡକାଷ୍ଟିଂ କିପରି କାମ କରେ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ବ୍ରଡକାଷ୍ଟ"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ଆପଣଙ୍କ ଆଖପାଖର କମ୍ପାଟିବଲ ବ୍ଲୁଟୁଥ ଡିଭାଇସ ଥିବା ଲୋକମାନେ ଆପଣ ବ୍ରଡକାଷ୍ଟ କରୁଥିବା ମିଡିଆ ଶୁଣିପାରିବେ"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ଏହି ସ୍କ୍ରିନ ବନ୍ଦ ହୋଇଯିବ"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ଫୋଲ୍ଡ କରାଯାଇପାରୁଥିବା ଡିଭାଇସକୁ ଅନଫୋଲ୍ଡ କରାଯାଉଛି"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ଫୋଲ୍ଡ କରାଯାଇପାରୁଥିବା ଡିଭାଇସକୁ ଫ୍ଲିପ କରାଯାଉଛି"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"ଷ୍ଟାଇଲସ ବେଟେରୀର ଚାର୍ଜ କମ ଅଛି"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index f6ad3ad01bac..c4dd6b455667 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"ਹੇਠਾਂ ਦੀ ਸੀਮਾ <xliff:g id="PERCENT">%1$d</xliff:g> ਫ਼ੀਸਦ"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ਖੱਬੇ ਪਾਸੇ ਵਾਲੀ ਸੀਮਾ <xliff:g id="PERCENT">%1$d</xliff:g> ਫ਼ੀਸਦ"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"ਸੱਜੇ ਪਾਸੇ ਵਾਲੀ ਸੀਮਾ <xliff:g id="PERCENT">%1$d</xliff:g> ਫ਼ੀਸਦ"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਰ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਜਾਰੀ ਹੈ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ਕਿਸੇ ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ ਸੈਸ਼ਨ ਲਈ ਚੱਲ ਰਹੀ ਸੂਚਨਾ"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> ਤੋਂ <xliff:g id="SONG_NAME">%1$s</xliff:g> ਚਲਾਓ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ਅਣਕੀਤਾ ਕਰੋ"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> \'ਤੇ ਚਲਾਉਣ ਲਈ ਨੇੜੇ ਲਿਜਾਓ"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"ਇੱਥੇ ਚਲਾਉਣ ਲਈ <xliff:g id="DEVICENAME">%1$s</xliff:g> ਦੇ ਨੇੜੇ ਜਾਓ"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> \'ਤੇ ਚਲਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"ਕੋਈ ਗੜਬੜ ਹੋ ਗਈ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"ਲੋਡ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"ਅਕਿਰਿਆਸ਼ੀਲ, ਐਪ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ਨਹੀਂ ਮਿਲਿਆ"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ਅਵਾਜ਼"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ਸਪੀਕਰ ਅਤੇ ਡਿਸਪਲੇਆਂ"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ਪ੍ਰਸਾਰਨ ਕਿਵੇਂ ਕੰਮ ਕਰਦਾ ਹੈ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ਪ੍ਰਸਾਰਨ"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ਅਨੁਰੂਪ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਨਾਲ ਨਜ਼ਦੀਕੀ ਲੋਕ ਤੁਹਾਡੇ ਵੱਲੋਂ ਪ੍ਰਸਾਰਨ ਕੀਤੇ ਜਾ ਰਹੇ ਮੀਡੀਆ ਨੂੰ ਸੁਣ ਸਕਦੇ ਹਨ"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ਇਹ ਸਕ੍ਰੀਨ ਬੰਦ ਹੋ ਜਾਵੇਗੀ"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ਮੋੜਨਯੋਗ ਡੀਵਾਈਸ ਨੂੰ ਖੋਲ੍ਹਿਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ਮੋੜਨਯੋਗ ਡੀਵਾਈਸ ਨੂੰ ਆਲੇ-ਦੁਆਲੇ ਫਲਿੱਪ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"ਸਟਾਈਲਸ ਦੀ ਬੈਟਰੀ ਘੱਟ ਹੈ"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 7cced9e01ba2..08a7ba15cdfc 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Przycięcie dolnej krawędzi o <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Przycięcie lewej krawędzi o <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Przycięcie prawej krawędzi o <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Nagrywanie ekranu"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Przetwarzam nagrywanie ekranu"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Stałe powiadomienie o sesji rejestrowania zawartości ekranu"</string>
@@ -377,7 +381,7 @@
<string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"Podczas udostępniania, nagrywania lub przesyłania treści aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ma dostęp do wszystkiego, co jest widoczne na ekranie lub odtwarzane na urządzeniu. Zachowaj ostrożność w przypadku haseł, danych do płatności, wiadomości i innych informacji poufnych."</string>
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Podczas udostępniania, nagrywania lub przesyłania treści aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ma dostęp do wszystkiego, co jest w niej wyświetlane lub odtwarzane. Zachowaj ostrożność w przypadku haseł, danych do płatności, wiadomości i innych informacji poufnych."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Dalej"</string>
- <string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Udostępnianie i nagrywanie za pomocą aplikacji"</string>
+ <string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Udostępnianie i nagrywanie aplikacji"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"Zezwolić tej aplikacji na udostępnianie lub nagrywanie?"</string>
<string name="media_projection_permission_dialog_system_service_warning_entire_screen" msgid="8801616203805837575">"Podczas udostępniania, nagrywania lub przesyłania treści ta aplikacja ma dostęp do wszystkiego, co jest widoczne na ekranie lub odtwarzane na urządzeniu. Zachowaj ostrożność w przypadku haseł, danych do płatności, wiadomości i innych informacji poufnych."</string>
<string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"Podczas udostępniania, nagrywania lub przesyłania treści ta aplikacja ma dostęp do wszystkiego, co jest w niej wyświetlane lub odtwarzane. Zachowaj ostrożność w przypadku haseł, danych do płatności, wiadomości i innych informacji poufnych."</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Odtwórz utwór <xliff:g id="SONG_NAME">%1$s</xliff:g> w aplikacji <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Cofnij"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Przysuń się bliżej, aby odtwarzać na urządzeniu <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Zbliż do urządzenia <xliff:g id="DEVICENAME">%1$s</xliff:g>, aby na nim odtwarzać"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Odtwarzam na ekranie <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Coś poszło nie tak. Spróbuj ponownie."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Wczytuję"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Nieaktywny, sprawdź aplikację"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nie znaleziono"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Głośność"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Głośniki i wyświetlacze"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jak działa transmitowanie"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transmisja"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osoby w pobliżu ze zgodnymi urządzeniami Bluetooth mogą słuchać transmitowanych przez Ciebie multimediów"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"* Ekran się wyłączy"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Składane urządzenie jest rozkładane"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Składane urządzenie jest obracane"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Słaba bateria w rysiku"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 41925480b3ad..fa5e83f37c8c 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Borda inferior em <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Borda esquerda em <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Borda direita em <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Capturas de tela do trabalho são salvas no app <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processando gravação de tela"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação contínua para uma sessão de gravação de tela"</string>
@@ -450,7 +452,7 @@
<string name="screen_pinning_title" msgid="9058007390337841305">"O app está fixado"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"Ela é mantida à vista até que seja liberada. Toque em Voltar e em Visão geral e mantenha essas opções pressionadas para liberar."</string>
<string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Ela é mantida à vista até que seja liberada. Toque em Voltar e em Início e mantenha essas opções pressionadas para liberar."</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Ele é mantido à vista até que seja liberado. Deslize para cima e mantenha pressionado para liberar."</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Ele é mantido à vista até que seja liberado. Deslize para cima pressione para liberar."</string>
<string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Ela é mantida à vista até que seja liberada. Toque em Visão geral e mantenha essa opção pressionada para liberar."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Ela é mantida à vista até que seja liberada. Toque em Início e mantenha essa opção pressionada para liberar."</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Dados pessoais podem ficar acessíveis (como contatos e conteúdo de e-mail)."</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> no app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desfazer"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Aproxime os dispositivos para tocar a mídia neste: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Aproxime-se do dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g> para abrir a mídia aqui"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Mídia aberta no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Algo deu errado. Tente novamente."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Carregando"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inativo, verifique o app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"O controle está indisponível"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Alto-falantes e telas"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transmitir"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas a você com dispositivos Bluetooth compatíveis podem ouvir a mídia que você está transmitindo"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Esta tela vai ser desativada"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo dobrável sendo aberto"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo dobrável sendo virado"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index ad93343fd081..fbedb3aa1cba 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Limite inferior de <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Limite esquerdo de <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Limite direito de <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"As capturas de ecrã do trabalho são guardadas na app <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Ficheiros"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravador de ecrã"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"A processar a gravação de ecrã"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação persistente de uma sessão de gravação de ecrã"</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproduzir <xliff:g id="SONG_NAME">%1$s</xliff:g> a partir da app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Anular"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Aproxime-se para reproduzir no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Aproxime-se do dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g> para reproduzir aqui"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"A reproduzir no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Algo correu mal. Tente novamente."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"A carregar"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inativa. Consulte a app."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado."</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"O controlo está indisponível"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altifalantes e ecrãs"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transmissão"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas de si com dispositivos Bluetooth compatíveis podem ouvir o conteúdo multimédia que está a transmitir"</string>
@@ -1015,5 +1019,6 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Este ecrã vai ser desligado"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo dobrável a ser desdobrado"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo dobrável a ser virado ao contrário"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da caneta stylus fraca"</string>
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> de bateria restante"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ligue a caneta stylus a um carregador"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 41925480b3ad..fa5e83f37c8c 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Borda inferior em <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Borda esquerda em <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Borda direita em <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Capturas de tela do trabalho são salvas no app <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processando gravação de tela"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação contínua para uma sessão de gravação de tela"</string>
@@ -450,7 +452,7 @@
<string name="screen_pinning_title" msgid="9058007390337841305">"O app está fixado"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"Ela é mantida à vista até que seja liberada. Toque em Voltar e em Visão geral e mantenha essas opções pressionadas para liberar."</string>
<string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Ela é mantida à vista até que seja liberada. Toque em Voltar e em Início e mantenha essas opções pressionadas para liberar."</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Ele é mantido à vista até que seja liberado. Deslize para cima e mantenha pressionado para liberar."</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Ele é mantido à vista até que seja liberado. Deslize para cima pressione para liberar."</string>
<string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Ela é mantida à vista até que seja liberada. Toque em Visão geral e mantenha essa opção pressionada para liberar."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Ela é mantida à vista até que seja liberada. Toque em Início e mantenha essa opção pressionada para liberar."</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Dados pessoais podem ficar acessíveis (como contatos e conteúdo de e-mail)."</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> no app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desfazer"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Aproxime os dispositivos para tocar a mídia neste: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Aproxime-se do dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g> para abrir a mídia aqui"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Mídia aberta no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Algo deu errado. Tente novamente."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Carregando"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inativo, verifique o app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"O controle está indisponível"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Alto-falantes e telas"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transmitir"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas a você com dispositivos Bluetooth compatíveis podem ouvir a mídia que você está transmitindo"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Esta tela vai ser desativada"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo dobrável sendo aberto"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo dobrável sendo virado"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index e4a1b0bcf2e0..deef0762f4d8 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Marginea de jos la <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Marginea stângă la <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Marginea dreaptă la <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Recorder pentru ecran"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Se procesează înregistrarea"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificare în curs pentru o sesiune de înregistrare a ecranului"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Redă <xliff:g id="SONG_NAME">%1$s</xliff:g> în <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Anulează"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Apropie-te pentru a reda pe <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Apropie-te de <xliff:g id="DEVICENAME">%1$s</xliff:g> ca să redai acolo"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Se redă pe <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"A apărut o eroare. Încearcă din nou."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Se încarcă"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Inactiv, verifică aplicația"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nu s-a găsit"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volum"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Difuzoare și afișaje"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cum funcționează transmisia"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transmite"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Persoanele din apropiere cu dispozitive Bluetooth compatibile pot asculta conținutul pe care îl transmiți"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Acest ecran se va dezactiva"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispozitiv pliabil care este desfăcut"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispozitiv pliabil care este întors"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Nivelul bateriei creionului este scăzut"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 9b4975e712e6..97d0d70c4a8c 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Граница снизу: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Граница слева: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Граница справа: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Запись видео с экрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обработка записи с экрана…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущее уведомление для записи видео с экрана"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Воспроизвести медиафайл \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" из приложения \"<xliff:g id="APP_LABEL">%2$s</xliff:g>\""</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Отменить"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Чтобы начать трансляцию на устройстве \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\", подойдите к нему ближе."</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Для воспроизведения на этом устройстве подойдите ближе к другому (<xliff:g id="DEVICENAME">%1$s</xliff:g>)."</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Воспроизводится на устройстве \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\"."</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Произошла ошибка. Повторите попытку."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Загрузка…"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Нет ответа. Проверьте приложение."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не найдено."</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Громкость"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Колонки и дисплеи"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Как работают трансляции"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Трансляция"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Находящиеся рядом с вами люди с совместимыми устройствами Bluetooth могут слушать медиафайлы, которые вы транслируете."</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Этот экран отключится"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Складное устройство в разложенном виде"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Перевернутое складное устройство"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Низкий заряд батареи стилуса"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 91ed10f66e1d..63acd771f61f 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"පහළ සීමාව සියයට <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"වම් සීමාව සියයට <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"දකුණු සීමාව සියයට <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"තිර රෙකෝඩරය"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"තිර පටිගත කිරීම සකසමින්"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"තිර පටිගත කිරීමේ සැසියක් සඳහා කෙරෙන දැනුම් දීම"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g> වෙතින් වාදනය කරන්න"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"පසුගමනය කරන්න"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> හි වාදනය කිරීමට වඩාත් ළං වන්න"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"මෙහි ක්‍රීඩා කිරීමට <xliff:g id="DEVICENAME">%1$s</xliff:g> වෙත වඩා සමීප වන්න"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> හි වාදනය කරමින්"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"යම් දෙයක් වැරදිණි. නැවත උත්සාහ කරන්න."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"පූරණය වේ"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"අක්‍රියයි, යෙදුම පරීක්ෂා කරන්න"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"හමු නොවිණි"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"හඬ පරිමාව"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ස්පීකර් සහ සංදර්ශක"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"විකාශනය ක්‍රියා කරන ආකාරය"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"විකාශනය"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ගැළපෙන බ්ලූටූත් උපාංග සහිත ඔබ අවට සිටින පුද්ගලයින්ට ඔබ විකාශනය කරන මාධ්‍යයට සවන් දිය හැකිය"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ මෙම තිරය ක්‍රියා විරහිත වනු ඇත"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"දිග හැරෙමින් පවතින නැමිය හැකි උපාංගය"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"වටා පෙරළෙමින් තිබෙන නැමිය හැකි උපාංගය"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"පන්හිඳ බැටරිය අඩුයි"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 4c3d8b816347..5a5d87a320ce 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"<xliff:g id="PERCENT">%1$d</xliff:g> %% dolnej hranice"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"<xliff:g id="PERCENT">%1$d</xliff:g> %% ľavej hranice"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"<xliff:g id="PERCENT">%1$d</xliff:g> %% pravej hranice"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Spracúva sa záznam obrazovky"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Zobrazuje sa upozornenie týkajúce sa relácie záznamu obrazovky"</string>
@@ -375,9 +379,9 @@
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"Celá obrazovka"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"Jedna aplikácia"</string>
<string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"Počas zdieľania, nahrávania alebo prenosu bude mať <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> prístup k všetkému na obrazovke, prípadne k obsahu, ktorý sa bude v zariadení prehrávať. Preto venujte zvýšenú pozornosť heslám, platobným údajom, správam a ďalším citlivým údajom."</string>
- <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Počas zdieľania, nahrávania alebo prenosu bude mať aplikácia <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> prístup k všetkému obsahu, ktorý sa v nej bude zobrazovať alebo prehrávať. Preto venujte zvýšenú pozornosť heslám, platobným údajom, správam a ďalším citlivým údajom."</string>
+ <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Keď zdieľate, nahrávate alebo prenášate nejakú aplikáciu, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> má prístup k všetkému obsahu, ktorý sa v aplikácii zobrazuje alebo prehráva. Dajte preto pozor na heslá, platobné údaje, osobné správy a iné citlivé údaje."</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"Pokračovať"</string>
- <string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Aplikácia na zdieľanie alebo nahrávanie"</string>
+ <string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"Vyberte aplikáciu, ktorú chcete zdieľať alebo nahrávať"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"Chcete povoliť tejto aplikácii zdieľať alebo nahrávať?"</string>
<string name="media_projection_permission_dialog_system_service_warning_entire_screen" msgid="8801616203805837575">"Počas zdieľania, nahrávania alebo prenosu bude mať táto aplikácia prístup k všetkému na obrazovke, prípadne k obsahu, ktorý sa bude v zariadení prehrávať. Venujte preto zvýšenú pozornosť heslám, platobným údajom, správam a ďalším citlivým údajom."</string>
<string name="media_projection_permission_dialog_system_service_warning_single_app" msgid="543310680568419338">"Počas zdieľania, nahrávania alebo prenosu bude mať táto aplikácia prístup k všetkému obsahu, ktorý sa v nej bude zobrazovať alebo prehrávať. Venujte preto zvýšenú pozornosť heslám, platobným údajom, správam či ďalším citlivým údajom."</string>
@@ -849,11 +853,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Prehrať skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> od interpreta <xliff:g id="ARTIST_NAME">%2$s</xliff:g> z aplikácie <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Prehrať skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> z aplikácie <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Späť"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Ak chcete prehrávať v zariadení <xliff:g id="DEVICENAME">%1$s</xliff:g>, priblížte sa"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Ak chcete prehrávať v zariadení <xliff:g id="DEVICENAME">%1$s</xliff:g>, priblížte sa k nemu"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Ak chcete prehrávať v zariadení <xliff:g id="DEVICENAME">%1$s</xliff:g>, priblížte sa k nemu"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Prehráva sa v zariadení <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Niečo sa pokazilo. Skúste to znova."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Načítava sa"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktívne, preverte aplikáciu"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nenájdené"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Hlasitosť"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Reproduktory a obrazovky"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Ako vysielanie funguje"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Vysielanie"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Ľudia v okolí s kompatibilnými zariadeniami s rozhraním Bluetooth si môžu vypočuť médiá, ktoré vysielate"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Táto obrazovka sa vypne"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Rozloženie skladacieho zariadenia"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Prevrátenie skladacieho zariadenia"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Stav batérie dotykového pera je nízky"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index df5dcb9059df..c16bf230a9b3 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Meja spodaj <xliff:g id="PERCENT">%1$d</xliff:g> odstotkov"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Meja levo <xliff:g id="PERCENT">%1$d</xliff:g> odstotkov"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Meja desno <xliff:g id="PERCENT">%1$d</xliff:g> odstotkov"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Snemalnik zaslona"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obdelava videoposnetka zaslona"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Nenehno obveščanje o seji snemanja zaslona"</string>
@@ -849,11 +853,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Predvajaj skladbo <xliff:g id="SONG_NAME">%1$s</xliff:g> izvajalca <xliff:g id="ARTIST_NAME">%2$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>."</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Predvajaj skladbo <xliff:g id="SONG_NAME">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>."</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Razveljavi"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Za predvajanje v napravi <xliff:g id="DEVICENAME">%1$s</xliff:g> bolj približajte telefon."</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Približajte napravi <xliff:g id="DEVICENAME">%1$s</xliff:g> za predvajanje v tej napravi"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Za predvajanje v napravi <xliff:g id="DEVICENAME">%1$s</xliff:g> bolj približajte telefon"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Predvajanje v napravi <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Prišlo je do napake. Poskusite znova."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Nalaganje"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, poglejte aplikacijo"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ni mogoče najti"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Glasnost"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvočniki in zasloni"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako deluje oddajanje"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Oddajanje"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osebe v bližini z združljivo napravo Bluetooth lahko poslušajo predstavnost, ki jo oddajate."</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ta zaslon se bo izklopil."</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Razpiranje zložljive naprave"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Obračanje zložljive naprave"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Skoraj prazna baterija pisala"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index ad2e596f849c..e9a6e8c9efb4 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Kufiri i poshtëm <xliff:g id="PERCENT">%1$d</xliff:g> për qind"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Kufiri i majtë <xliff:g id="PERCENT">%1$d</xliff:g> për qind"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Kufiri i djathtë <xliff:g id="PERCENT">%1$d</xliff:g> për qind"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Regjistruesi i ekranit"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Regjistrimi i ekranit po përpunohet"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Njoftim i vazhdueshëm për një seancë regjistrimi të ekranit"</string>
@@ -372,7 +376,7 @@
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Do të fillosh regjistrimin ose transmetimin?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Fillo regjistrimin ose transmetimin me <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"Të lejohet <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> të shpërndajë ose regjistrojë?"</string>
- <string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"Ekran i plotë"</string>
+ <string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"Të gjithë ekranin"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"Vetëm një aplikacion"</string>
<string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"Gjatë shpërndarjes, regjistrimit ose transmetimit, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ka qasje te çdo gjë e dukshme në ekranin tënd ose që po luhet në pajisjen tënde. Prandaj ki kujdes me fjalëkalimet, detajet e pagesës, mesazhet ose informacione të tjera të ndjeshme."</string>
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"Gjatë shpërndarjes, regjistrimit ose transmetimit të një aplikacioni, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ka qasje te çdo gjë e dukshme në ekranin tënd ose që po luhet në atë aplikacion. Prandaj, ki kujdes me fjalëkalimet, detajet e pagesës, mesazhet ose informacione të tjera të ndjeshme."</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Luaj <xliff:g id="SONG_NAME">%1$s</xliff:g> nga <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Zhbëj"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Afrohu për të luajtur në <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Afrohu te <xliff:g id="DEVICENAME">%1$s</xliff:g> për ta luajtur këtu"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Po luhet në <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Ndodhi një gabim. Provo përsëri."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Po ngarkohet"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Joaktive, kontrollo aplikacionin"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nuk u gjet"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volumi"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altoparlantët dhe ekranet"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Si funksionon transmetimi"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Transmetimi"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personat në afërsi me ty me pajisje të përputhshme me Bluetooth mund të dëgjojnë median që ti po transmeton"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ky ekran do të fiket"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Pajisja e palosshme duke u hapur"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Pajisja e palosshme duke u rrotulluar"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria e stilolapsit në nivel të ulët"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index ef6bb4597104..153ebbf3948b 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Доња ивица <xliff:g id="PERCENT">%1$d</xliff:g> посто"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Лева ивица <xliff:g id="PERCENT">%1$d</xliff:g> посто"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Десна ивица <xliff:g id="PERCENT">%1$d</xliff:g> посто"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Снимач екрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обрађујемо видео снимка екрана"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Обавештење о сесији снимања екрана је активно"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Пустите <xliff:g id="SONG_NAME">%1$s</xliff:g> из апликације <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Опозови"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Приближите да бисте пуштали музику на: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Приближите се уређају <xliff:g id="DEVICENAME">%1$s</xliff:g> да бисте на њему пуштали"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Пушта се на уређају <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Дошло је до грешке. Пробајте поново."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Учитава се"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивно. Видите апликацију"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Није пронађено"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Звук"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Звучници и екрани"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Како функционише емитовање"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Емитовање"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Људи у близини са компатибилним Bluetooth уређајима могу да слушају медијски садржај који емитујете"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Овај екран ће се искључити"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Уређај на преклоп се отвара"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Уређај на преклоп се обрће"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Низак ниво батерије писаљке"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index c98273926204..38c7719c13ae 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Nedre gräns: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Vänster gräns: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Höger gräns: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Jobbskärmbilder sparas i <xliff:g id="APP">%1$s</xliff:g>-appen"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Filer"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skärminspelare"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandlar skärminspelning"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Avisering om att skärminspelning pågår"</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spela upp <xliff:g id="SONG_NAME">%1$s</xliff:g> från <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Ångra"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Flytta närmare för att spela upp på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Flytta dig närmare <xliff:g id="DEVICENAME">%1$s</xliff:g> om du vill spela här"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Spelas upp på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Något gick fel. Försök igen."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Läser in"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"surfplatta"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv, kolla appen"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Hittades inte"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Styrning är inte tillgänglig"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volym"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Högtalare och skärmar"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Förslag på enheter"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Så fungerar utsändning"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Utsändning"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personer i närheten med kompatibla Bluetooth-enheter kan lyssna på medieinnehåll som du sänder ut"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Den här skärmen inaktiveras"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"En vikbar enhet viks upp"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"En vikbar enhet vänds"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"E-pennans batterinivå är låg"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 5e33bf4d82fc..e43acb8533ee 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Mpaka wa sehemu ya chini wa asilimia <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Mpaka wa sehemu ya kushoto wa asilimia <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Mpaka wa sehemu ya kulia wa asilimia <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Kinasa Skrini"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Inachakata rekodi ya skrini"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Arifa inayoendelea ya kipindi cha kurekodi skrini"</string>
@@ -849,11 +853,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Cheza <xliff:g id="SONG_NAME">%1$s</xliff:g> ulioimbwa na <xliff:g id="ARTIST_NAME">%2$s</xliff:g> katika <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Cheza <xliff:g id="SONG_NAME">%1$s</xliff:g> katika <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Tendua"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Sogea karibu ili ucheze kwenye <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Sogeza karibu na <xliff:g id="DEVICENAME">%1$s</xliff:g> ili kucheza hapa"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Sogeza karibu ili ucheze kwenye <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Inacheza kwenye <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Hitilafu fulani imetokea. Jaribu tena."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Inapakia"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Haitumiki, angalia programu"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Hakipatikani"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Sauti"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Spika na Skrini"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jinsi utangazaji unavyofanya kazi"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Tangaza"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Watu walio karibu nawe wenye vifaa oanifu vya Bluetooth wanaweza kusikiliza maudhui unayoyatangaza"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Skrini hii itajizima"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Kifaa kinachokunjwa kikikunjuliwa"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Kifaa kinachokunjwa kikigeuzwa"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Chaji ya betri ya Stylus imepungua"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sw360dp/dimens.xml b/packages/SystemUI/res/values-sw360dp/dimens.xml
index 65ca70bac0dc..03365b3d8c4f 100644
--- a/packages/SystemUI/res/values-sw360dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw360dp/dimens.xml
@@ -25,5 +25,8 @@
<!-- Home Controls -->
<dimen name="global_actions_side_margin">12dp</dimen>
+
+ <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
+ <dimen name="biometric_auth_pattern_view_size">298dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw392dp-land/dimens.xml b/packages/SystemUI/res/values-sw392dp-land/dimens.xml
new file mode 100644
index 000000000000..1e26a699f85a
--- /dev/null
+++ b/packages/SystemUI/res/values-sw392dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <!-- Lock pattern view size, align sysui biometric_auth_pattern_view_size -->
+ <dimen name="biometric_auth_pattern_view_size">248dp</dimen>
+ <dimen name="biometric_auth_pattern_view_max_size">248dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-sw392dp/dimens.xml b/packages/SystemUI/res/values-sw392dp/dimens.xml
index 78279ca4f520..96af3c13f32e 100644
--- a/packages/SystemUI/res/values-sw392dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw392dp/dimens.xml
@@ -24,5 +24,8 @@
<!-- Home Controls -->
<dimen name="global_actions_side_margin">16dp</dimen>
+
+ <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
+ <dimen name="biometric_auth_pattern_view_size">298dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw410dp-land/dimens.xml b/packages/SystemUI/res/values-sw410dp-land/dimens.xml
new file mode 100644
index 000000000000..c4d9b9b92f57
--- /dev/null
+++ b/packages/SystemUI/res/values-sw410dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <!-- Lock pattern view size, align sysui biometric_auth_pattern_view_size -->
+ <dimen name="biometric_auth_pattern_view_size">248dp</dimen>
+ <dimen name="biometric_auth_pattern_view_max_size">348dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-sw410dp/dimens.xml b/packages/SystemUI/res/values-sw410dp/dimens.xml
index 7da47e5089be..ff6e005a94c7 100644
--- a/packages/SystemUI/res/values-sw410dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw410dp/dimens.xml
@@ -27,4 +27,6 @@
<dimen name="global_actions_grid_item_side_margin">12dp</dimen>
<dimen name="global_actions_grid_item_height">72dp</dimen>
+ <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
+ <dimen name="biometric_auth_pattern_view_size">348dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 6c7cab51f440..5d78e4e1a44c 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -28,6 +28,7 @@
<!-- QS-->
<dimen name="qs_panel_padding_top">16dp</dimen>
+ <dimen name="qs_panel_padding">24dp</dimen>
<dimen name="qs_content_horizontal_padding">24dp</dimen>
<dimen name="qs_horizontal_margin">24dp</dimen>
<!-- in split shade qs_tiles_page_horizontal_margin should be equal of qs_horizontal_margin/2,
diff --git a/packages/SystemUI/res/values-sw600dp-land/styles.xml b/packages/SystemUI/res/values-sw600dp-land/styles.xml
index 8148d3dfaf7d..c535c6416106 100644
--- a/packages/SystemUI/res/values-sw600dp-land/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/styles.xml
@@ -18,10 +18,10 @@
<style name="AuthCredentialPatternContainerStyle">
<item name="android:gravity">center</item>
- <item name="android:maxHeight">420dp</item>
- <item name="android:maxWidth">420dp</item>
- <item name="android:minHeight">200dp</item>
- <item name="android:minWidth">200dp</item>
+ <item name="android:maxHeight">@dimen/biometric_auth_pattern_view_max_size</item>
+ <item name="android:maxWidth">@dimen/biometric_auth_pattern_view_max_size</item>
+ <item name="android:minHeight">@dimen/biometric_auth_pattern_view_size</item>
+ <item name="android:minWidth">@dimen/biometric_auth_pattern_view_size</item>
<item name="android:paddingHorizontal">120dp</item>
<item name="android:paddingVertical">40dp</item>
</style>
diff --git a/packages/SystemUI/res/values-sw600dp-port/styles.xml b/packages/SystemUI/res/values-sw600dp-port/styles.xml
index 771de08cb360..32eefa7316b2 100644
--- a/packages/SystemUI/res/values-sw600dp-port/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/styles.xml
@@ -26,10 +26,10 @@
<style name="AuthCredentialPatternContainerStyle">
<item name="android:gravity">center</item>
- <item name="android:maxHeight">420dp</item>
- <item name="android:maxWidth">420dp</item>
- <item name="android:minHeight">200dp</item>
- <item name="android:minWidth">200dp</item>
+ <item name="android:maxHeight">@dimen/biometric_auth_pattern_view_max_size</item>
+ <item name="android:maxWidth">@dimen/biometric_auth_pattern_view_max_size</item>
+ <item name="android:minHeight">@dimen/biometric_auth_pattern_view_size</item>
+ <item name="android:minWidth">@dimen/biometric_auth_pattern_view_size</item>
<item name="android:paddingHorizontal">180dp</item>
<item name="android:paddingVertical">80dp</item>
</style>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 599bf30a5135..9bc0dde49b4c 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -92,4 +92,6 @@
<dimen name="lockscreen_shade_status_bar_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
<dimen name="lockscreen_shade_keyguard_transition_distance">@dimen/lockscreen_shade_media_transition_distance</dimen>
+ <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
+ <dimen name="biometric_auth_pattern_view_size">348dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index 3fc59e38ec6c..122806a1af8f 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -27,7 +27,7 @@
<dimen name="status_view_margin_horizontal">24dp</dimen>
- <dimen name="qs_media_session_height_expanded">251dp</dimen>
+ <dimen name="qs_media_session_height_expanded">184dp</dimen>
<dimen name="qs_content_horizontal_padding">40dp</dimen>
<dimen name="qs_horizontal_margin">40dp</dimen>
<!-- in split shade qs_tiles_page_horizontal_margin should be equal of qs_horizontal_margin/2,
@@ -36,8 +36,8 @@
<dimen name="qs_tiles_page_horizontal_margin">20dp</dimen>
<!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
- <dimen name="qs_media_rec_icon_top_margin">27dp</dimen>
- <dimen name="qs_media_rec_album_size">152dp</dimen>
+ <dimen name="qs_media_rec_icon_top_margin">16dp</dimen>
+ <dimen name="qs_media_rec_album_size">112dp</dimen>
<dimen name="qs_media_rec_album_side_margin">16dp</dimen>
<dimen name="lockscreen_shade_max_over_scroll_amount">42dp</dimen>
diff --git a/packages/SystemUI/res/values-sw720dp-land/styles.xml b/packages/SystemUI/res/values-sw720dp-land/styles.xml
index f9ed67d89de7..6a70ebd07ad2 100644
--- a/packages/SystemUI/res/values-sw720dp-land/styles.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/styles.xml
@@ -18,10 +18,10 @@
<style name="AuthCredentialPatternContainerStyle">
<item name="android:gravity">center</item>
- <item name="android:maxHeight">420dp</item>
- <item name="android:maxWidth">420dp</item>
- <item name="android:minHeight">200dp</item>
- <item name="android:minWidth">200dp</item>
+ <item name="android:maxHeight">@dimen/biometric_auth_pattern_view_max_size</item>
+ <item name="android:maxWidth">@dimen/biometric_auth_pattern_view_max_size</item>
+ <item name="android:minHeight">@dimen/biometric_auth_pattern_view_size</item>
+ <item name="android:minWidth">@dimen/biometric_auth_pattern_view_size</item>
<item name="android:paddingHorizontal">120dp</item>
<item name="android:paddingVertical">40dp</item>
</style>
diff --git a/packages/SystemUI/res/values-sw720dp-port/styles.xml b/packages/SystemUI/res/values-sw720dp-port/styles.xml
index 78d299c483e6..0a46e08da22a 100644
--- a/packages/SystemUI/res/values-sw720dp-port/styles.xml
+++ b/packages/SystemUI/res/values-sw720dp-port/styles.xml
@@ -26,10 +26,10 @@
<style name="AuthCredentialPatternContainerStyle">
<item name="android:gravity">center</item>
- <item name="android:maxHeight">420dp</item>
- <item name="android:maxWidth">420dp</item>
- <item name="android:minHeight">200dp</item>
- <item name="android:minWidth">200dp</item>
+ <item name="android:maxHeight">@dimen/biometric_auth_pattern_view_max_size</item>
+ <item name="android:maxWidth">@dimen/biometric_auth_pattern_view_max_size</item>
+ <item name="android:minHeight">@dimen/biometric_auth_pattern_view_size</item>
+ <item name="android:minWidth">@dimen/biometric_auth_pattern_view_size</item>
<item name="android:paddingHorizontal">240dp</item>
<item name="android:paddingVertical">120dp</item>
</style>
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index 07050171470a..927059aa7e40 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -22,5 +22,8 @@
<dimen name="controls_padding_horizontal">75dp</dimen>
<dimen name="large_screen_shade_header_height">56dp</dimen>
+
+ <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
+ <dimen name="biometric_auth_pattern_view_size">348dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw800dp/dimens.xml b/packages/SystemUI/res/values-sw800dp/dimens.xml
new file mode 100644
index 000000000000..0d82217456e4
--- /dev/null
+++ b/packages/SystemUI/res/values-sw800dp/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources>
+
+ <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
+ <dimen name="biometric_auth_pattern_view_size">348dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 6e14c2630a03..cc58ddb35c36 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"கீழ் எல்லை <xliff:g id="PERCENT">%1$d</xliff:g> சதவீதம்"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"இடது எல்லை <xliff:g id="PERCENT">%1$d</xliff:g> சதவீதம்"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"வலது எல்லை <xliff:g id="PERCENT">%1$d</xliff:g> சதவீதம்"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"ஸ்கிரீன் ரெக்கார்டர்"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ஸ்க்ரீன் ரெக்கார்டிங் செயலாக்கப்படுகிறது"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"திரை ரெக்கார்டிங் அமர்விற்கான தொடர் அறிவிப்பு"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> பாடலை <xliff:g id="APP_LABEL">%2$s</xliff:g> ஆப்ஸில் பிளேசெய்"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"செயல்தவிர்"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> சாதனத்தில் இயக்க உங்கள் சாதனத்தை அருகில் எடுத்துச் செல்லுங்கள்"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"இங்கு பிளே செய்ய உங்கள் சாதனத்தை <xliff:g id="DEVICENAME">%1$s</xliff:g> சாதனத்திற்கு அருகில் நகர்த்துங்கள்"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> சாதனத்தில் பிளே ஆகிறது"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"ஏதோ தவறாகிவிட்டது. மீண்டும் முயலவும்."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"ஏற்றுகிறது"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"செயலில் இல்லை , சரிபார்க்கவும்"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"இல்லை"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ஒலியளவு"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ஸ்பீக்கர்கள் &amp; டிஸ்ப்ளேக்கள்"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"பிராட்காஸ்ட் எவ்வாறு செயல்படுகிறது?"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"பிராட்காஸ்ட்"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"நீங்கள் பிராட்காஸ்ட் செய்யும் மீடியாவை அருகிலுள்ளவர்கள் இணக்கமான புளூடூத் சாதனங்கள் மூலம் கேட்கலாம்"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ இந்தத் திரை ஆஃப் ஆகிவிடும்"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"மடக்கத்தக்க சாதனம் திறக்கப்படுகிறது"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"மடக்கத்தக்க சாதனம் ஃபிளிப் செய்யப்பட்டு திருப்பப்படுகிறது"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"ஸ்டைலஸின் பேட்டரி குறைவாக உள்ளது"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index d4e458f78e28..1df7542232a5 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"దిగువ సరిహద్దు <xliff:g id="PERCENT">%1$d</xliff:g> శాతం"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ఎడమ వైపు సరిహద్దు <xliff:g id="PERCENT">%1$d</xliff:g> శాతం"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"కుడి వైపు సరిహద్దు <xliff:g id="PERCENT">%1$d</xliff:g> శాతం"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"వర్క్ స్క్రీన్‌షాట్‌లు <xliff:g id="APP">%1$s</xliff:g> యాప్‌లో సేవ్ చేయబడతాయి"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ఫైల్స్"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"స్క్రీన్ రికార్డర్"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"స్క్రీన్ రికార్డింగ్ అవుతోంది"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"స్క్రీన్ రికార్డ్ సెషన్ కోసం ఆన్‌గోయింగ్ నోటిఫికేషన్"</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> నుండి <xliff:g id="SONG_NAME">%1$s</xliff:g>‌ను ప్లే చేయండి"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"చర్య రద్దు"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>‌లో ప్లే చేయడానికి దగ్గరగా వెళ్లండి"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"ఇక్కడ ప్లే చేయడానికి <xliff:g id="DEVICENAME">%1$s</xliff:g>కి దగ్గరగా వెళ్లండి"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g>లో ప్లే అవుతోంది"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"ఏదో తప్పు జరిగింది. మళ్లీ ట్రై చేయండి."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"లోడ్ అవుతోంది"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"టాబ్లెట్"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ఇన్‌యాక్టివ్, యాప్ చెక్ చేయండి"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"కనుగొనబడలేదు"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"కంట్రోల్ అందుబాటులో లేదు"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"వాల్యూమ్"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"స్పీకర్‌లు &amp; డిస్‌ప్లేలు"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"సూచించబడిన పరికరాలు"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ప్రసారం కావడం అనేది ఎలా పని చేస్తుంది"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ప్రసారం"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"మీకు సమీపంలో ఉన్న వ్యక్తులు అనుకూలత ఉన్న బ్లూటూత్ పరికరాలతో మీరు ప్రసారం చేస్తున్న మీడియాను వినగలరు"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ఈ స్క్రీన్ ఆఫ్ అవుతుంది"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"మడవగల పరికరం విప్పబడుతోంది"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"మడవగల పరికరం చుట్టూ తిప్పబడుతోంది"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"తక్కువ స్టైలస్ బ్యాటరీ"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index f8a16bd26483..771a906bc6ec 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"ขอบเขตด้านล่าง <xliff:g id="PERCENT">%1$d</xliff:g> เปอร์เซ็นต์"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"ขอบเขตด้านซ้าย <xliff:g id="PERCENT">%1$d</xliff:g> เปอร์เซ็นต์"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"ขอบเขตด้านขวา <xliff:g id="PERCENT">%1$d</xliff:g> เปอร์เซ็นต์"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"ภาพหน้าจองานจะบันทึกอยู่ในแอป <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ไฟล์"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"โปรแกรมบันทึกหน้าจอ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"กำลังประมวลผลการอัดหน้าจอ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการบันทึกหน้าจอ"</string>
@@ -849,12 +851,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"เปิดเพลง <xliff:g id="SONG_NAME">%1$s</xliff:g> ของ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> จาก <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"เปิดเพลง <xliff:g id="SONG_NAME">%1$s</xliff:g> จาก <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"เลิกทำ"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"ขยับเข้ามาใกล้ขึ้นเพื่อเล่นใน <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"ขยับไปใกล้ <xliff:g id="DEVICENAME">%1$s</xliff:g> มากขึ้นเพื่อเล่นที่นี่"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"ขยับไปใกล้มากขึ้นเพื่อเล่นใน <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"กำลังเล่นใน <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"เกิดข้อผิดพลาด โปรดลองอีกครั้ง"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"กำลังโหลด"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"แท็บเล็ต"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ไม่มีการใช้งาน โปรดตรวจสอบแอป"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ไม่พบ"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"ใช้การควบคุมไม่ได้"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ระดับเสียง"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ลำโพงและจอแสดงผล"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"อุปกรณ์ที่แนะนำ"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"วิธีการทำงานของการออกอากาศ"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"ประกาศ"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ผู้ที่อยู่ใกล้คุณและมีอุปกรณ์บลูทูธที่รองรับสามารถรับฟังสื่อที่คุณกำลังออกอากาศได้"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ หน้าจอนี้จะปิดไป"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"อุปกรณ์ที่พับได้กำลังกางออก"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"อุปกรณ์ที่พับได้กำลังพลิกไปมา"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"แบตเตอรี่สไตลัสเหลือน้อย"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 3578aa78c96b..aa58d00df76a 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"<xliff:g id="PERCENT">%1$d</xliff:g> (na) porsyento sa hangganan sa ibaba"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"<xliff:g id="PERCENT">%1$d</xliff:g> (na) porsyento sa hangganan sa kaliwa"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"<xliff:g id="PERCENT">%1$d</xliff:g> (na) porsyento sa hangganan sa kanan"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Naka-save ang mga screenshot sa trabaho sa <xliff:g id="APP">%1$s</xliff:g> app"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Mga File"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Recorder ng Screen"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Pinoproseso screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Kasalukuyang notification para sa session ng pag-record ng screen"</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"I-play ang <xliff:g id="SONG_NAME">%1$s</xliff:g> mula sa <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"I-undo"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Lumapit pa para mag-play sa <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Lumapit sa <xliff:g id="DEVICENAME">%1$s</xliff:g> para mag-play rito"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Nagpe-play sa <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Nagkaproblema. Subukan ulit."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Naglo-load"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Hindi aktibo, tingnan ang app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Hindi nahanap"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Hindi available ang kontrol"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Mga Speaker at Display"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Mga Iminumungkahing Device"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Paano gumagana ang pag-broadcast"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Makakapakinig ang mga taong malapit sa iyo na may mga compatible na Bluetooth device sa media na bino-broadcast mo"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Mag-o-off ang screen na ito"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Ina-unfold na foldable na device"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Fini-flip na foldable na device"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Paubos na ang baterya ng stylus"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 64431ad25980..2ee29cf925a9 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Alt sınır yüzde <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Sol sınır yüzde <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Sağ sınır yüzde <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Ekran Kaydedicisi"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran kaydı işleniyor"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekran kaydı oturumu için devam eden bildirim"</string>
@@ -849,11 +853,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> uygulamasından <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, <xliff:g id="SONG_NAME">%1$s</xliff:g> şarkısını çal"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> uygulamasından <xliff:g id="SONG_NAME">%1$s</xliff:g> şarkısını çal"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Geri al"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında çalmak için yaklaşın"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Burada oynatmak için <xliff:g id="DEVICENAME">%1$s</xliff:g> cihazına yaklaşın"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında oynatmak için yaklaşın"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında oynatılıyor"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Bir sorun oldu. Tekrar deneyin."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Yükleme"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Devre dışı, uygulamaya bakın"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Bulunamadı"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Ses düzeyi"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Hoparlörler ve Ekranlar"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Yayınlamanın işleyiş şekli"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Anons"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Yakınınızda ve uyumlu Bluetooth cihazları olan kişiler yayınladığınız medya içeriğini dinleyebilir"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ * Bu ekran kapatılacak"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Katlanabilir cihaz açılıyor"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Katlanabilir cihaz döndürülüyor"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Ekran kaleminin pil seviyesi düşük"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 9eba61007965..ae22d087f07e 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Знизу на <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Зліва на <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Справа на <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Запис відео з екрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обробка записування екрана"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Сповіщення про сеанс запису екрана"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Увімкнути пісню \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" у додатку <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Відмінити"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Щоб відтворити контент на пристрої <xliff:g id="DEVICENAME">%1$s</xliff:g>, наблизьтеся до нього"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Наблизьтеся до пристрою <xliff:g id="DEVICENAME">%1$s</xliff:g>, щоб відтворити медіафайли на ньому"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Відтворюється на пристрої <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Сталася помилка. Повторіть спробу."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Завантаження"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивно, перейдіть у додаток"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не знайдено"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Гучність"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Колонки й екрани"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Як працює трансляція"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Трансляція"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Люди поблизу, які мають сумісні пристрої з Bluetooth, можуть слухати медіаконтент, який ви транслюєте."</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Цей екран вимкнеться"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Розкладний пристрій у розкладеному стані"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Розкладний пристрій обертається"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Низький заряд акумулятора стилуса"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index cfcdeca3a2c9..f02797b6c4ae 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"نیچے کا احاطہ <xliff:g id="PERCENT">%1$d</xliff:g> فیصد"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"بایاں احاطہ <xliff:g id="PERCENT">%1$d</xliff:g> فیصد"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"دایاں احاطہ <xliff:g id="PERCENT">%1$d</xliff:g> فیصد"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"اسکرین ریکارڈر"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"سکرین ریکارڈنگ پروسیس ہورہی ہے"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"اسکرین ریکارڈ سیشن کیلئے جاری اطلاع"</string>
@@ -849,11 +853,13 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> سے <xliff:g id="ARTIST_NAME">%2$s</xliff:g> کا <xliff:g id="SONG_NAME">%1$s</xliff:g> چلائیں"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> سے <xliff:g id="SONG_NAME">%1$s</xliff:g> چلائیں"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"کالعدم کریں"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> پر چلانے کے لیے قریب کریں"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"یہاں چلانے کے ليے <xliff:g id="DEVICENAME">%1$s</xliff:g> کے قریب جائیں"</string>
- <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> پر چل رہا ہے"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"‫<xliff:g id="DEVICENAME">%1$s</xliff:g> پر چلانے کے لیے قریب کریں"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
+ <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"‫<xliff:g id="DEVICENAME">%1$s</xliff:g> پر چل رہا ہے"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"کچھ غلط ہوگیا۔ پھر کوشش کریں۔"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"لوڈ ہو رہا ہے"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"غیر فعال، ایپ چیک کریں"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"نہیں ملا"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"والیوم"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"اسپیکرز اور ڈسپلیز"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"براڈکاسٹنگ کیسے کام کرتا ہے"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"براڈکاسٹ"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"موافق بلوٹوتھ آلات کے ساتھ آپ کے قریبی لوگ آپ کے نشر کردہ میڈیا کو سن سکتے ہیں"</string>
@@ -1015,5 +1023,6 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ یہ اسکرین آف ہو جائے گی"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"فولڈ ہونے والے آلے کو کھولا جا رہا ہے"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"فولڈ ہونے والے آلے کو گھمایا جا رہا ہے"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"اسٹائلس بیٹری کم ہے"</string>
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> بیٹری باقی ہے"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"اپنے اسٹائلس کو چارجر منسلک کریں"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 83085190c1fa..0d5ad6082831 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Quyi chegara <xliff:g id="PERCENT">%1$d</xliff:g> foiz"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Chap chegara <xliff:g id="PERCENT">%1$d</xliff:g> foiz"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Oʻng chegara <xliff:g id="PERCENT">%1$d</xliff:g> foiz"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Ish skrinshotlari <xliff:g id="APP">%1$s</xliff:g> ilovasida saqlanadi"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fayllar"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekrandan yozib olish"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran yozib olinmoqda"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekrandan yozib olish seansi uchun joriy bildirishnoma"</string>
@@ -849,12 +851,12 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> ilovasida ijro etish: <xliff:g id="SONG_NAME">%1$s</xliff:g> – <xliff:g id="ARTIST_NAME">%2$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> ilovasida ijro etilmoqda: <xliff:g id="SONG_NAME">%1$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Qaytarish"</string>
- <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>da ijro etish uchun yaqinroq keling"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Bu yerda ijro qilish uchun <xliff:g id="DEVICENAME">%1$s</xliff:g>qurilmasiga yaqinlashtiring"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> qurilmasida ijro qilish uchun unga yaqinlashing"</string>
+ <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Shu yerda ijro qilish uchun <xliff:g id="DEVICENAME">%1$s</xliff:g>ga yaqinroq suring"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"<xliff:g id="DEVICENAME">%1$s</xliff:g> qurilmasida ijro qilinmoqda"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Xatolik yuz berdi. Qayta urining."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Yuklanmoqda"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"planshet"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Nofaol. Ilovani tekshiring"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Topilmadi"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Boshqarish imkonsiz"</string>
@@ -878,6 +880,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Tovush balandligi"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Karnaylar va displeylar"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Taklif qilingan qurilmalar"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Translatsiya qanday ishlaydi"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Translatsiya"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Atrofingizdagi mos Bluetooth qurilmasiga ega foydalanuvchilar siz translatsiya qilayotgan mediani tinglay olishadi"</string>
@@ -1015,5 +1018,6 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Bu ekran oʻchiriladi"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Buklanadigan qurilma ochilmoqda"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Buklanadigan qurilma aylantirilmoqda"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Stilus batareyasi kam"</string>
+ <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Batareya quvvati: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Stilusni quvvat manbaiga ulang"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 80aa82293e65..c2e27d45851e 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Cạnh dưới cùng <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Cạnh trái <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Cạnh phải <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Trình ghi màn hình"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Đang xử lý video ghi màn hình"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Thông báo đang diễn ra về phiên ghi màn hình"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Phát <xliff:g id="SONG_NAME">%1$s</xliff:g> trên <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Hủy"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Đưa thiết bị đến gần hơn để phát trên <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Di chuyển đến gần <xliff:g id="DEVICENAME">%1$s</xliff:g> hơn để phát tại đây"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Đang phát trên <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Đã xảy ra lỗi. Hãy thử lại."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Đang tải"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Không hoạt động, hãy kiểm tra ứng dụng"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Không tìm thấy"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Âm lượng"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Loa và màn hình"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cách tính năng truyền hoạt động"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Truyền"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Những người ở gần có thiết bị Bluetooth tương thích có thể nghe nội dung nghe nhìn bạn đang truyền"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Màn hình này sẽ tắt"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Thiết bị có thể gập lại đang được mở ra"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Thiết bị có thể gập lại đang được lật ngược"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Bút cảm ứng bị yếu pin"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index a449e9e332cd..b401a8c9c5bc 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"底部边界百分之 <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"左侧边界百分之 <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"右侧边界百分之 <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"屏幕录制器"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"正在处理屏幕录制视频"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"持续显示屏幕录制会话通知"</string>
@@ -374,8 +378,8 @@
<string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"允许 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 分享或录制吗?"</string>
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"整个屏幕"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"单个应用"</string>
- <string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"在您进行分享、录制或投射时,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 可以访问您的屏幕显示或设备播放的所有内容。因此,请注意保护密码、付款信息、消息或其他敏感信息。"</string>
- <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"在您进行分享、录制或投射时,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 可以访问通过此应用显示或播放的所有内容。因此,请注意保护密码、付款信息、消息或其他敏感信息。"</string>
+ <string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"当您进行分享、录制或投屏时,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 可以访问您的屏幕上显示的或设备上播放的所有内容。因此,请注意保护密码、付款信息、消息或其他敏感信息。"</string>
+ <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"当您对一款应用进行分享、录制或投屏时,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 可以访问该应用中显示或播放的所有内容。因此,请注意保护密码、付款信息、消息或其他敏感信息。"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"继续"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"分享或录制应用"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"是否允许此应用进行分享或录制?"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"通过<xliff:g id="APP_LABEL">%2$s</xliff:g>播放《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"撤消"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"若要在“<xliff:g id="DEVICENAME">%1$s</xliff:g>”上播放,请靠近这台设备"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"若要在此设备上播放,请靠近“<xliff:g id="DEVICENAME">%1$s</xliff:g>”"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"正在“<xliff:g id="DEVICENAME">%1$s</xliff:g>”上播放"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"出了点问题,请重试。"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"正在加载"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"无效,请检查应用"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"未找到"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"音量"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"音箱和显示屏"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"广播的运作方式"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"广播"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"附近使用兼容蓝牙设备的用户可以收听您广播的媒体内容"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ 此屏幕将会关闭"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"正在展开可折叠设备"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"正在翻转可折叠设备"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"触控笔电池电量低"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 4749e3bc9a97..0d54f6b2294b 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -91,13 +91,15 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"下方邊界 <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"左方邊界 <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"右方邊界 <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"工作的螢幕截圖會儲存在「<xliff:g id="APP">%1$s</xliff:g>」應用程式"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"檔案"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"螢幕畫面錄影工具"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"正在處理螢幕錄影內容"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"持續顯示錄影畫面工作階段通知"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"要開始錄製嗎?"</string>
<string name="screenrecord_description" msgid="1123231719680353736">"錄影時,Android 系統可擷取螢幕上顯示或裝置播放的任何敏感資料,包括密碼、付款資料、相片、訊息和音訊。"</string>
<string name="screenrecord_option_entire_screen" msgid="1732437834603426934">"錄製整個螢幕畫面"</string>
- <string name="screenrecord_option_single_app" msgid="5954863081500035825">"錄製單一應用程式"</string>
+ <string name="screenrecord_option_single_app" msgid="5954863081500035825">"錄製一個應用程式"</string>
<string name="screenrecord_warning_entire_screen" msgid="8141407178104195610">"進行錄製時,Android 可存取顯示在螢幕畫面上或在裝置上播放的所有內容。因此請謹慎處理密碼、付款資料、訊息或其他敏感資料。"</string>
<string name="screenrecord_warning_single_app" msgid="7760723997065948283">"錄製應用程式時,Android 可存取在該應用程式中顯示或播放的所有內容。因此請謹慎處理密碼、付款資料、訊息或其他敏感資料。"</string>
<string name="screenrecord_start_recording" msgid="348286842544768740">"開始錄製"</string>
@@ -373,8 +375,8 @@
<string name="media_projection_dialog_title" msgid="3316063622495360646">"要使用「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」開始錄影或投放嗎?"</string>
<string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"允許 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 分享或錄製嗎?"</string>
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"整個螢幕畫面"</string>
- <string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"單一應用程式"</string>
- <string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"進行分享、錄製或投放時,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 可存取顯示在螢幕畫面上或在裝置上播放的所有內容。因此請謹慎處理密碼、付款資料、訊息或其他敏感資料。"</string>
+ <string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"一個應用程式"</string>
+ <string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"當您分享、錄製或投放應用程式時,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 可存取在螢幕畫面上顯示或在裝置上播放的所有內容。因此請小心保管密碼、付款資料、訊息或其他敏感資料。"</string>
<string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"進行分享、錄製或投放時,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 可存取顯示在螢幕畫面上或在裝置上播放的所有內容。因此請謹慎處理密碼、付款資料、訊息或其他敏感資料。"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"繼續"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"分享或錄製應用程式"</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"在 <xliff:g id="APP_LABEL">%2$s</xliff:g> 播放《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"復原"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"如要在「<xliff:g id="DEVICENAME">%1$s</xliff:g>」上播放,請靠近一點"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"如要在此裝置上播放,請靠近「<xliff:g id="DEVICENAME">%1$s</xliff:g>」"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"正在「<xliff:g id="DEVICENAME">%1$s</xliff:g>」上播放"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"發生錯誤,請再試一次。"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"載入中"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"平板電腦"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"已停用,請檢查應用程式"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"找不到"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"無法使用控制功能"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"音量"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"喇叭和螢幕"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"建議的裝置"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"廣播運作方式"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"廣播"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"附近有兼容藍牙裝置的人可收聽您正在廣播的媒體內容"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ 此螢幕將關閉"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"正在展開折疊式裝置"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"正在翻轉折疊式裝置"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"觸控筆電量不足"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 4972aee02a80..6a78a93dc069 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -91,6 +91,8 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"下方邊界百分之 <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"左側邊界百分之 <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"右側邊界百分之 <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
+ <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"工作的螢幕截圖會儲存在「<xliff:g id="APP">%1$s</xliff:g>」應用程式"</string>
+ <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"檔案"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"螢幕錄影器"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"處理螢幕錄影內容"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"持續顯示螢幕畫面錄製工作階段通知"</string>
@@ -374,8 +376,8 @@
<string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"允許 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 分享或錄製?"</string>
<string name="media_projection_permission_dialog_option_entire_screen" msgid="392086473225692983">"整個螢幕畫面"</string>
<string name="media_projection_permission_dialog_option_single_app" msgid="1591110238124910521">"單一應用程式"</string>
- <string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"進行分享、錄製或投放時,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 可以存取顯示在螢幕畫面上或在裝置上播放的所有內容。因此請謹慎處理密碼、付款資料、訊息或其他機密資訊。"</string>
- <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"進行分享、錄製或投放應用程式時,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 可以存取在該應用程式中顯示或播放的所有內容。因此請謹慎處理密碼、付款資料、訊息或其他機密資訊。"</string>
+ <string name="media_projection_permission_dialog_warning_entire_screen" msgid="3989078820637452717">"分享、錄製或投放時,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 可以存取顯示在螢幕畫面上或在裝置上播放的所有內容。因此請謹慎處理密碼、付款資料、訊息或其他機密資訊。"</string>
+ <string name="media_projection_permission_dialog_warning_single_app" msgid="1659532781536753059">"分享、錄製或投放應用程式時,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 可以存取在應用程式中顯示或播放的所有內容。因此請謹慎處理密碼、付款資料、訊息或其他機密資訊。"</string>
<string name="media_projection_permission_dialog_continue" msgid="1827799658916736006">"繼續"</string>
<string name="media_projection_permission_app_selector_title" msgid="894251621057480704">"分享或錄製應用程式"</string>
<string name="media_projection_permission_dialog_system_service_title" msgid="6827129613741303726">"要允許這個應用程式分享或錄製嗎?"</string>
@@ -850,11 +852,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"透過「<xliff:g id="APP_LABEL">%2$s</xliff:g>」播放〈<xliff:g id="SONG_NAME">%1$s</xliff:g>〉"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"復原"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"如要在「<xliff:g id="DEVICENAME">%1$s</xliff:g>」上播放,請靠近一點"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"如要在這部裝置上播放,請移到更靠近「<xliff:g id="DEVICENAME">%1$s</xliff:g>」的位置"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"正在「<xliff:g id="DEVICENAME">%1$s</xliff:g>」上播放"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"發生錯誤,請再試一次。"</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
- <skip />
+ <string name="media_transfer_loading" msgid="5544017127027152422">"載入中"</string>
+ <string name="media_ttt_default_device_type" msgid="4457646436153370169">"平板電腦"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"無效,請查看應用程式"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"找不到控制項"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"無法使用控制項"</string>
@@ -878,6 +881,7 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"音量"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"喇叭和螢幕"</string>
+ <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"建議的裝置"</string>
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"廣播功能的運作方式"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"廣播"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"如果附近的人有相容的藍牙裝置,就可以聽到你正在廣播的媒體內容"</string>
@@ -1015,5 +1019,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ 這麼做會關閉這個螢幕"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"正在展開的折疊式裝置"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"正在翻轉折疊式裝置"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"觸控筆電力不足"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index d9cf44b3fe3d..311fe8b99598 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -91,6 +91,10 @@
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Iphesenti elingu-<xliff:g id="PERCENT">%1$d</xliff:g> lomngcele ophansi"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Iphesenti elingu-<xliff:g id="PERCENT">%1$d</xliff:g> lomngcele ongakwesobunxele"</string>
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Iphesenti elingu-<xliff:g id="PERCENT">%1$d</xliff:g> lomngcele ongakwesokudla"</string>
+ <!-- no translation found for screenshot_work_profile_notification (2812417845875653929) -->
+ <skip />
+ <!-- no translation found for screenshot_default_files_app_name (8721579578575161912) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Irekhoda yesikrini"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Icubungula okokuqopha iskrini"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Isaziso esiqhubekayo seseshini yokurekhoda isikrini"</string>
@@ -850,10 +854,12 @@
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Dlala i-<xliff:g id="SONG_NAME">%1$s</xliff:g> kusuka ku-<xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Hlehlisa"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Sondeza eduze ukudlala ku-<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Sondela eduze ne-<xliff:g id="DEVICENAME">%1$s</xliff:g> ukuze udlale lapha"</string>
+ <!-- no translation found for media_move_closer_to_end_cast (7302555909119374738) -->
+ <skip />
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Idlala ku-<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_transfer_failed" msgid="7955354964610603723">"Kukhona okungahambanga kahle. Zama futhi."</string>
- <!-- no translation found for media_transfer_loading (5544017127027152422) -->
+ <string name="media_transfer_loading" msgid="5544017127027152422">"Iyalayisha"</string>
+ <!-- no translation found for media_ttt_default_device_type (4457646436153370169) -->
<skip />
<string name="controls_error_timeout" msgid="794197289772728958">"Akusebenzi, hlola uhlelo lokusebenza"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ayitholakali"</string>
@@ -878,6 +884,8 @@
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Ivolumu"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
<string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Izipikha Neziboniso"</string>
+ <!-- no translation found for media_output_group_title_suggested_device (4157186235837903826) -->
+ <skip />
<string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Indlela ukusakaza okusebenza ngayo"</string>
<string name="media_output_broadcast" msgid="3555580945878071543">"Sakaza"</string>
<string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Abantu abaseduze nawe abanamadivayisi e-Bluetooth ahambisanayo bangalalela imidiya oyisakazayo"</string>
@@ -1015,5 +1023,8 @@
<string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Lesi sikrini sizovala"</b></string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Idivayisi egoqekayo iyembulwa"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Idivayisi egoqekayo iphendulwa nxazonke"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"Ibhethri le-stylus liphansi"</string>
+ <!-- no translation found for stylus_battery_low_percentage (1620068112350141558) -->
+ <skip />
+ <!-- no translation found for stylus_battery_low_subtitle (3583843128908823273) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 077ef0f16fe2..e8a5e7ed8546 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -668,6 +668,16 @@
<item>17</item> <!-- WAKE_REASON_BIOMETRIC -->
</integer-array>
+ <!-- Whether to support posture listening for face auth, default is 0(DEVICE_POSTURE_UNKNOWN)
+ means systemui will try listening on all postures.
+ 0 : DEVICE_POSTURE_UNKNOWN
+ 1 : DEVICE_POSTURE_CLOSED
+ 2 : DEVICE_POSTURE_HALF_OPENED
+ 3 : DEVICE_POSTURE_OPENED
+ 4 : DEVICE_POSTURE_FLIPPED
+ -->
+ <integer name="config_face_auth_supported_posture">0</integer>
+
<!-- Whether the communal service should be enabled -->
<bool name="config_communalServiceEnabled">false</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0fc1d13ffdbe..890d96444b04 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -973,6 +973,10 @@
<!-- Biometric Auth Credential values -->
<dimen name="biometric_auth_icon_size">48dp</dimen>
+ <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
+ <dimen name="biometric_auth_pattern_view_size">348dp</dimen>
+ <dimen name="biometric_auth_pattern_view_max_size">348dp</dimen>
+
<!-- Starting text size in sp of batteryLevel for wireless charging animation -->
<item name="wireless_charging_anim_battery_level_text_size_start" format="float" type="dimen">
0
@@ -1051,6 +1055,7 @@
<dimen name="qs_media_disabled_seekbar_height">1dp</dimen>
<dimen name="qs_media_enabled_seekbar_height">2dp</dimen>
<dimen name="qs_media_app_icon_size">24dp</dimen>
+ <dimen name="qs_media_explicit_indicator_icon_size">13dp</dimen>
<dimen name="qs_media_session_enabled_seekbar_vertical_padding">15dp</dimen>
<dimen name="qs_media_session_disabled_seekbar_vertical_padding">16dp</dimen>
@@ -1287,6 +1292,9 @@
<!-- LOCKSCREEN -> DREAMING transition: Amount to shift lockscreen content on entering -->
<dimen name="lockscreen_to_dreaming_transition_lockscreen_translation_y">-40dp</dimen>
+ <!-- GONE -> DREAMING transition: Amount to shift lockscreen content on entering -->
+ <dimen name="gone_to_dreaming_transition_lockscreen_translation_y">-40dp</dimen>
+
<!-- LOCKSCREEN -> OCCLUDED transition: Amount to shift lockscreen content on entering -->
<dimen name="lockscreen_to_occluded_transition_lockscreen_translation_y">-40dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 22d4c6d651e2..43dea0a06898 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1455,10 +1455,10 @@
<string name="notification_conversation_summary_low">No sound or vibration and appears lower in conversation section</string>
<!-- [CHAR LIMIT=150] Notification Importance title: normal importance level summary -->
- <string name="notification_channel_summary_default">May ring or vibrate based on phone settings</string>
+ <string name="notification_channel_summary_default">May ring or vibrate based on device settings</string>
<!-- [CHAR LIMIT=150] Conversation Notification Importance title: normal conversation level, with bubbling summary -->
- <string name="notification_channel_summary_default_with_bubbles">May ring or vibrate based on phone settings. Conversations from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubble by default.</string>
+ <string name="notification_channel_summary_default_with_bubbles">May ring or vibrate based on device settings. Conversations from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubble by default.</string>
<!-- [CHAR LIMIT=150] Notification Importance title: automatic importance level summary -->
<string name="notification_channel_summary_automatic">Have the system determine if this notification should make sound or vibration</string>
@@ -2359,10 +2359,10 @@
<!-- Text to ask the user to move their device closer to a different device (deviceName) in order to play media on the different device. [CHAR LIMIT=75] -->
<string name="media_move_closer_to_start_cast">Move closer to play on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
<!-- Text to ask the user to move their device closer to a different device (deviceName) in order to transfer media from the different device and back onto the current device. [CHAR LIMIT=75] -->
- <string name="media_move_closer_to_end_cast">Move closer to <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g> to play here</string>
+ <string name="media_move_closer_to_end_cast">To play here, move closer to <xliff:g id="deviceName" example="tablet">%1$s</xliff:g></string>
<!-- Text informing the user that their media is now playing on a different device (deviceName). [CHAR LIMIT=50] -->
<string name="media_transfer_playing_different_device">Playing on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
- <!-- Text informing the user that the media transfer has failed because something went wrong. [CHAR LIsMIT=50] -->
+ <!-- Text informing the user that the media transfer has failed because something went wrong. [CHAR LIMIT=50] -->
<string name="media_transfer_failed">Something went wrong. Try again.</string>
<!-- Text to indicate that a media transfer is currently in-progress, aka loading. [CHAR LIMIT=NONE] -->
<string name="media_transfer_loading">Loading</string>
@@ -2777,4 +2777,19 @@
<string name="stylus_battery_low_percentage"><xliff:g id="percentage" example="16%">%s</xliff:g> battery remaining</string>
<!-- Subtitle for the notification sent when a stylus battery is low. [CHAR LIMIT=none]-->
<string name="stylus_battery_low_subtitle">Connect your stylus to a charger</string>
+
+ <!-- Switch to work profile dialer app for placing a call dialog. -->
+ <!-- Text for Switch to work profile dialog's Title. Switch to work profile dialog guide users to make call from work
+ profile dialer app as it's not possible to make call from current profile due to an admin policy. [CHAR LIMIT=60] -->
+ <string name="call_from_work_profile_title">Can\'t call from this profile</string>
+ <!-- Text for switch to work profile for call dialog to guide users to make call from work
+ profile dialer app as it's not possible to make call from current profile due to an admin policy. [CHAR LIMIT=NONE]
+ -->
+ <string name="call_from_work_profile_text">Your work policy allows you to make phone calls only from the work profile</string>
+ <!-- Label for the button to switch to work profile for placing call. Switch to work profile dialog guide users to make call from work
+ profile dialer app as it's not possible to make call from current profile due to an admin policy.[CHAR LIMIT=60] -->
+ <string name="call_from_work_profile_action">Switch to work profile</string>
+ <!-- Label for the close button on switch to work profile dialog. Switch to work profile dialog guide users to make call from work
+ profile dialer app as it's not possible to make call from current profile due to an admin policy.[CHAR LIMIT=60] -->
+ <string name="call_from_work_profile_close">Close</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index b11b6d633f14..9846fc251a27 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -251,11 +251,12 @@
<style name="AuthCredentialPatternContainerStyle">
<item name="android:gravity">center</item>
- <item name="android:maxHeight">420dp</item>
- <item name="android:maxWidth">420dp</item>
- <item name="android:minHeight">200dp</item>
- <item name="android:minWidth">200dp</item>
- <item name="android:padding">20dp</item>
+ <item name="android:maxHeight">@dimen/biometric_auth_pattern_view_max_size</item>
+ <item name="android:maxWidth">@dimen/biometric_auth_pattern_view_max_size</item>
+ <item name="android:minHeight">@dimen/biometric_auth_pattern_view_size</item>
+ <item name="android:minWidth">@dimen/biometric_auth_pattern_view_size</item>
+ <item name="android:paddingHorizontal">32dp</item>
+ <item name="android:paddingVertical">20dp</item>
</style>
<style name="AuthCredentialPinPasswordContainerStyle">
diff --git a/packages/SystemUI/res/xml/large_screen_shade_header.xml b/packages/SystemUI/res/xml/large_screen_shade_header.xml
index 06d425c57577..bf576dc5790b 100644
--- a/packages/SystemUI/res/xml/large_screen_shade_header.xml
+++ b/packages/SystemUI/res/xml/large_screen_shade_header.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,105 +14,73 @@
~ limitations under the License.
-->
-<ConstraintSet
- xmlns:android="http://schemas.android.com/apk/res/android"
+<ConstraintSet xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/large_screen_header_constraint">
- <Constraint
- android:id="@+id/clock">
+ <Constraint android:id="@+id/clock">
<Layout
android:layout_width="wrap_content"
android:layout_height="0dp"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@id/date"
- app:layout_constraintHorizontal_bias="0"
- />
- <Transform
- android:scaleX="1"
- android:scaleY="1"
- />
+ app:layout_constraintStart_toEndOf="@id/begin_guide"
+ app:layout_constraintTop_toTopOf="parent" />
+ <PropertySet android:alpha="1" />
</Constraint>
- <Constraint
- android:id="@+id/date">
+ <Constraint android:id="@+id/date">
<Layout
android:layout_width="wrap_content"
android:layout_height="0dp"
- app:layout_constraintStart_toEndOf="@id/clock"
- app:layout_constraintEnd_toStartOf="@id/carrier_group"
- app:layout_constraintTop_toTopOf="parent"
+ android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintHorizontal_bias="0"
- />
+ app:layout_constraintStart_toEndOf="@id/clock"
+ app:layout_constraintTop_toTopOf="parent" />
+ <PropertySet android:alpha="1" />
</Constraint>
- <Constraint
- android:id="@+id/carrier_group">
+ <Constraint android:id="@+id/carrier_group">
<Layout
- app:layout_constraintWidth_min="48dp"
android:layout_width="0dp"
android:layout_height="0dp"
- app:layout_constrainedWidth="true"
android:layout_gravity="end|center_vertical"
- android:layout_marginStart="8dp"
- app:layout_constraintStart_toEndOf="@id/date"
- app:layout_constraintEnd_toStartOf="@id/statusIcons"
- app:layout_constraintTop_toTopOf="@id/clock"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintHorizontal_bias="1"
- />
- <PropertySet
- android:alpha="1"
- />
+ app:layout_constraintEnd_toStartOf="@id/statusIcons"
+ app:layout_constraintStart_toEndOf="@id/date"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintWidth_default="wrap"
+ app:layout_constraintWidth_min="48dp" />
+ <PropertySet android:alpha="1" />
</Constraint>
- <Constraint
- android:id="@+id/statusIcons">
+ <Constraint android:id="@+id/statusIcons">
<Layout
- app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height"
android:layout_width="wrap_content"
android:layout_height="@dimen/large_screen_shade_header_min_height"
- app:layout_constraintStart_toEndOf="@id/carrier_group"
- app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
- app:layout_constraintTop_toTopOf="@id/clock"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintHorizontal_bias="1"
- />
- <PropertySet
- android:alpha="1"
- />
+ app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintEnd_toEndOf="@id/carrier_group"/>
+ <PropertySet android:alpha="1" />
</Constraint>
- <Constraint
- android:id="@+id/batteryRemainingIcon">
+ <Constraint android:id="@+id/batteryRemainingIcon">
<Layout
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height"
- app:layout_constraintStart_toEndOf="@id/statusIcons"
- app:layout_constraintEnd_toStartOf="@id/privacy_container"
- app:layout_constraintTop_toTopOf="@id/clock"
app:layout_constraintBottom_toBottomOf="parent"
- />
- <PropertySet
- android:alpha="1"
- />
+ app:layout_constraintEnd_toStartOf="@id/privacy_container"
+ app:layout_constraintTop_toTopOf="parent" />
+ <PropertySet android:alpha="1" />
</Constraint>
- <Constraint
- android:id="@+id/privacy_container">
+ <Constraint android:id="@+id/privacy_container">
<Layout
android:layout_width="wrap_content"
android:layout_height="@dimen/large_screen_shade_header_min_height"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="@id/date"
- app:layout_constraintBottom_toBottomOf="@id/date"
- app:layout_constraintStart_toEndOf="@id/batteryRemainingIcon"
- app:layout_constraintHorizontal_bias="1"
- />
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/end_guide"
+ app:layout_constraintTop_toTopOf="parent" />
</Constraint>
-
</ConstraintSet> \ No newline at end of file
diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml
index 1eb621e0368b..d9c81af54a12 100644
--- a/packages/SystemUI/res/xml/media_session_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_session_collapsed.xml
@@ -66,6 +66,21 @@
app:layout_constraintTop_toBottomOf="@id/icon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0" />
+
+ <Constraint
+ android:id="@+id/media_explicit_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/qs_media_info_spacing"
+ android:layout_marginBottom="@dimen/qs_media_padding"
+ android:layout_marginTop="0dp"
+ app:layout_constraintStart_toStartOf="@id/header_title"
+ app:layout_constraintEnd_toStartOf="@id/header_artist"
+ app:layout_constraintTop_toTopOf="@id/header_artist"
+ app:layout_constraintBottom_toTopOf="@id/media_action_barrier_top"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintHorizontal_chainStyle="packed" />
+
<Constraint
android:id="@+id/header_artist"
android:layout_width="wrap_content"
@@ -75,9 +90,8 @@
app:layout_constraintEnd_toStartOf="@id/action_button_guideline"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toBottomOf="@id/header_title"
- app:layout_constraintStart_toStartOf="@id/header_title"
- app:layout_constraintVertical_bias="0"
- app:layout_constraintHorizontal_bias="0" />
+ app:layout_constraintStart_toEndOf="@id/media_explicit_indicator"
+ app:layout_constraintVertical_bias="0" />
<Constraint
android:id="@+id/actionPlayPause"
diff --git a/packages/SystemUI/res/xml/media_session_expanded.xml b/packages/SystemUI/res/xml/media_session_expanded.xml
index 7de0a5e0e8c4..0cdc0f9505bc 100644
--- a/packages/SystemUI/res/xml/media_session_expanded.xml
+++ b/packages/SystemUI/res/xml/media_session_expanded.xml
@@ -58,6 +58,21 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@id/header_artist"
app:layout_constraintHorizontal_bias="0" />
+
+ <Constraint
+ android:id="@+id/media_explicit_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/qs_media_info_spacing"
+ android:layout_marginBottom="@dimen/qs_media_padding"
+ android:layout_marginTop="0dp"
+ app:layout_constraintStart_toStartOf="@id/header_title"
+ app:layout_constraintEnd_toStartOf="@id/header_artist"
+ app:layout_constraintTop_toTopOf="@id/header_artist"
+ app:layout_constraintBottom_toTopOf="@id/media_action_barrier_top"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintHorizontal_chainStyle="packed"/>
+
<Constraint
android:id="@+id/header_artist"
android:layout_width="wrap_content"
@@ -67,10 +82,9 @@
android:layout_marginTop="0dp"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toStartOf="@id/actionPlayPause"
- app:layout_constraintStart_toStartOf="@id/header_title"
+ app:layout_constraintStart_toEndOf="@id/media_explicit_indicator"
app:layout_constraintBottom_toTopOf="@id/media_action_barrier_top"
- app:layout_constraintVertical_bias="0"
- app:layout_constraintHorizontal_bias="0" />
+ app:layout_constraintVertical_bias="0" />
<Constraint
android:id="@+id/actionPlayPause"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/hardware/InputDevice.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/hardware/InputDevice.kt
new file mode 100644
index 000000000000..a9a5cf974f95
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/hardware/InputDevice.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.hardware
+
+import android.view.InputDevice
+
+/**
+ * Returns true if [InputDevice] is electronic components to allow a user to use an active stylus in
+ * the host device or a passive stylus is detected by the host device.
+ */
+val InputDevice.isInternalStylusSource: Boolean
+ get() = isAnyStylusSource && !isExternal
+
+/** Returns true if [InputDevice] is an active stylus. */
+val InputDevice.isExternalStylusSource: Boolean
+ get() = isAnyStylusSource && isExternal
+
+/**
+ * Returns true if [InputDevice] supports any stylus source.
+ *
+ * @see InputDevice.isInternalStylusSource
+ * @see InputDevice.isExternalStylusSource
+ */
+val InputDevice.isAnyStylusSource: Boolean
+ get() = supportsSource(InputDevice.SOURCE_STYLUS)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/hardware/InputManager.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/hardware/InputManager.kt
new file mode 100644
index 000000000000..f020b4ec4983
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/hardware/InputManager.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.hardware
+
+import android.hardware.input.InputManager
+import android.view.InputDevice
+
+/**
+ * Gets information about all input devices in the system and returns as a lazy [Sequence].
+ *
+ * For performance reasons, it is preferred to operate atop the returned [Sequence] to ensure each
+ * operation is executed on an element-per-element basis yet customizable.
+ *
+ * For example:
+ * ```kotlin
+ * val stylusDevices = inputManager.getInputDeviceSequence().filter {
+ * it.supportsSource(InputDevice.SOURCE_STYLUS)
+ * }
+ *
+ * val hasInternalStylus = stylusDevices.any { it.isInternal }
+ * val hasExternalStylus = stylusDevices.any { !it.isInternal }
+ * ```
+ *
+ * @return a [Sequence] of [InputDevice].
+ */
+fun InputManager.getInputDeviceSequence(): Sequence<InputDevice> =
+ inputDeviceIds.asSequence().mapNotNull { getInputDevice(it) }
+
+/**
+ * Returns the first [InputDevice] matching the given predicate, or null if no such [InputDevice]
+ * was found.
+ */
+fun InputManager.findInputDevice(predicate: (InputDevice) -> Boolean): InputDevice? =
+ getInputDeviceSequence().find { predicate(it) }
+
+/**
+ * Returns true if [any] [InputDevice] matches with [predicate].
+ *
+ * For example:
+ * ```kotlin
+ * val hasStylusSupport = inputManager.hasInputDevice { it.isStylusSupport() }
+ * val hasStylusPen = inputManager.hasInputDevice { it.isStylusPen() }
+ * ```
+ */
+fun InputManager.hasInputDevice(predicate: (InputDevice) -> Boolean): Boolean =
+ getInputDeviceSequence().any { predicate(it) }
+
+/** Returns true if host device has any [InputDevice] where [InputDevice.isInternalStylusSource]. */
+fun InputManager.hasInternalStylusSource(): Boolean = hasInputDevice { it.isInternalStylusSource }
+
+/** Returns true if host device has any [InputDevice] where [InputDevice.isExternalStylusSource]. */
+fun InputManager.hasExternalStylusSource(): Boolean = hasInputDevice { it.isExternalStylusSource }
+
+/** Returns true if host device has any [InputDevice] where [InputDevice.isAnyStylusSource]. */
+fun InputManager.hasAnyStylusSource(): Boolean = hasInputDevice { it.isAnyStylusSource }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index fd41cb0630dd..6bfaf5e49820 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -283,17 +283,6 @@ public class ActivityManagerWrapper {
}
/**
- * @return whether screen pinning is active.
- */
- public boolean isScreenPinningActive() {
- try {
- return getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED;
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /**
* @return whether screen pinning is enabled.
*/
public boolean isScreenPinningEnabled() {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 8af934f66b2a..dd52cfbdc80f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -16,6 +16,7 @@
package com.android.systemui.shared.system;
+import android.annotation.NonNull;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.app.TaskStackListener;
@@ -27,6 +28,8 @@ import android.os.Trace;
import android.util.Log;
import android.window.TaskSnapshot;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.os.SomeArgs;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -43,15 +46,51 @@ public class TaskStackChangeListeners {
private final Impl mImpl;
+ /**
+ * Proxies calls to the given handler callback synchronously for testing purposes.
+ */
+ private static class TestSyncHandler extends Handler {
+ private Handler.Callback mCb;
+
+ public TestSyncHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ public void setCallback(Handler.Callback cb) {
+ mCb = cb;
+ }
+
+ @Override
+ public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
+ return mCb.handleMessage(msg);
+ }
+ }
+
private TaskStackChangeListeners() {
mImpl = new Impl(Looper.getMainLooper());
}
+ private TaskStackChangeListeners(Handler h) {
+ mImpl = new Impl(h);
+ }
+
public static TaskStackChangeListeners getInstance() {
return INSTANCE;
}
/**
+ * Returns an instance of the listeners that can be called upon synchronously for testsing
+ * purposes.
+ */
+ @VisibleForTesting
+ public static TaskStackChangeListeners getTestInstance() {
+ TestSyncHandler h = new TestSyncHandler();
+ TaskStackChangeListeners l = new TaskStackChangeListeners(h);
+ h.setCallback(l.mImpl);
+ return l;
+ }
+
+ /**
* Registers a task stack listener with the system.
* This should be called on the main thread.
*/
@@ -71,7 +110,15 @@ public class TaskStackChangeListeners {
}
}
- private static class Impl extends TaskStackListener implements Handler.Callback {
+ /**
+ * Returns an instance of the listener to call upon from tests.
+ */
+ @VisibleForTesting
+ public TaskStackListener getListenerImpl() {
+ return mImpl;
+ }
+
+ private class Impl extends TaskStackListener implements Handler.Callback {
private static final int ON_TASK_STACK_CHANGED = 1;
private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
@@ -104,10 +151,14 @@ public class TaskStackChangeListeners {
private final Handler mHandler;
private boolean mRegistered;
- Impl(Looper looper) {
+ private Impl(Looper looper) {
mHandler = new Handler(looper, this);
}
+ private Impl(Handler handler) {
+ mHandler = handler;
+ }
+
public void addListener(TaskStackChangeListener listener) {
synchronized (mTaskStackListeners) {
mTaskStackListeners.add(listener);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt
index 7f2933e44b32..c9e57b45612c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt
@@ -15,21 +15,51 @@
package com.android.systemui.unfold.system
import android.app.ActivityManager
+import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration
+import android.os.Trace
+import com.android.systemui.shared.system.TaskStackChangeListener
+import com.android.systemui.shared.system.TaskStackChangeListeners
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
-class ActivityManagerActivityTypeProvider @Inject constructor(
- private val activityManager: ActivityManager
-) : CurrentActivityTypeProvider {
+class ActivityManagerActivityTypeProvider
+@Inject
+constructor(private val activityManager: ActivityManager) : CurrentActivityTypeProvider {
override val isHomeActivity: Boolean?
- get() {
- val activityType = activityManager.getRunningTasks(/* maxNum= */ 1)
- ?.getOrNull(0)?.topActivityType ?: return null
+ get() = _isHomeActivity
- return activityType == WindowConfiguration.ACTIVITY_TYPE_HOME
+ private var _isHomeActivity: Boolean? = null
+
+
+ override fun init() {
+ _isHomeActivity = activityManager.isOnHomeActivity()
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(taskStackChangeListener)
+ }
+
+ override fun uninit() {
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(taskStackChangeListener)
+ }
+
+ private val taskStackChangeListener =
+ object : TaskStackChangeListener {
+ override fun onTaskMovedToFront(taskInfo: RunningTaskInfo) {
+ _isHomeActivity = taskInfo.isHomeActivity()
+ }
+ }
+
+ private fun RunningTaskInfo.isHomeActivity(): Boolean =
+ topActivityType == WindowConfiguration.ACTIVITY_TYPE_HOME
+
+ private fun ActivityManager.isOnHomeActivity(): Boolean? {
+ try {
+ Trace.beginSection("isOnHomeActivity")
+ return getRunningTasks(/* maxNum= */ 1)?.firstOrNull()?.isHomeActivity()
+ } finally {
+ Trace.endSection()
}
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
index 24ae42ae4db2..fe607e16661c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
@@ -19,7 +19,7 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
import com.android.systemui.unfold.config.UnfoldTransitionConfig
-import com.android.systemui.unfold.dagger.UnfoldBackground
+import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
@@ -56,6 +56,6 @@ abstract class SystemUnfoldSharedModule {
abstract fun mainHandler(@Main handler: Handler): Handler
@Binds
- @UnfoldBackground
+ @UnfoldSingleThreadBg
abstract fun backgroundExecutor(@UiBackground executor: Executor): Executor
}
diff --git a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
index 5bb9367fa4a5..e0cf7b6a2bc4 100644
--- a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
+++ b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
@@ -50,6 +50,7 @@ import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_RESET
import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_VISIBILITY_CHANGED
import com.android.keyguard.InternalFaceAuthReasons.NON_STRONG_BIOMETRIC_ALLOWED_CHANGED
import com.android.keyguard.InternalFaceAuthReasons.OCCLUDING_APP_REQUESTED
+import com.android.keyguard.InternalFaceAuthReasons.POSTURE_CHANGED
import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN
import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN
import com.android.keyguard.InternalFaceAuthReasons.RETRY_AFTER_HW_UNAVAILABLE
@@ -126,6 +127,7 @@ private object InternalFaceAuthReasons {
const val STRONG_AUTH_ALLOWED_CHANGED = "Face auth stopped because strong auth allowed changed"
const val NON_STRONG_BIOMETRIC_ALLOWED_CHANGED =
"Face auth stopped because non strong biometric allowed changed"
+ const val POSTURE_CHANGED = "Face auth started/stopped due to device posture changed."
}
/**
@@ -173,6 +175,7 @@ constructor(private val id: Int, val reason: String, var extraInfo: Int = 0) :
return PowerManager.wakeReasonToString(extraInfo)
}
},
+ @UiEvent(doc = POSTURE_CHANGED) FACE_AUTH_UPDATED_POSTURE_CHANGED(1265, POSTURE_CHANGED),
@Deprecated(
"Not a face auth trigger.",
ReplaceWith(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
index deead1959b8a..1a06b5f1c767 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
@@ -39,6 +39,7 @@ data class KeyguardFaceListenModel(
var keyguardGoingAway: Boolean = false,
var listeningForFaceAssistant: Boolean = false,
var occludingAppRequestingFaceAuth: Boolean = false,
+ val postureAllowsListening: Boolean = false,
var primaryUser: Boolean = false,
var secureCameraLaunched: Boolean = false,
var supportsDetect: Boolean = false,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index d4ca8e34fb32..ea84438bf4ba 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -29,6 +29,9 @@ import android.view.View;
import android.view.View.OnKeyListener;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
+import android.window.OnBackAnimationCallback;
+
+import androidx.annotation.NonNull;
import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
@@ -394,6 +397,14 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
}
/**
+ * @return the {@link OnBackAnimationCallback} to animate this view during a back gesture.
+ */
+ @NonNull
+ public OnBackAnimationCallback getBackCallback() {
+ return mKeyguardSecurityContainerController.getBackCallback();
+ }
+
+ /**
* Allows the media keys to work when the keyguard is showing.
* The media keys should be of no interest to the actual keyguard view(s),
* so intercepting them here should not be of any harm.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 5d7a6f122e69..e4f85db3971e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -32,6 +32,7 @@ import static androidx.constraintlayout.widget.ConstraintSet.START;
import static androidx.constraintlayout.widget.ConstraintSet.TOP;
import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT;
+import static com.android.systemui.animation.InterpolatorsAndroidX.DECELERATE_QUINT;
import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
import static java.lang.Integer.max;
@@ -73,6 +74,8 @@ import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
+import android.window.BackEvent;
+import android.window.OnBackAnimationCallback;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
@@ -135,7 +138,9 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
private static final float MIN_DRAG_SIZE = 10;
// How much to scale the default slop by, to avoid accidental drags.
private static final float SLOP_SCALE = 4f;
-
+ @VisibleForTesting
+ // How much the view scales down to during back gestures.
+ static final float MIN_BACK_SCALE = 0.9f;
@VisibleForTesting
KeyguardSecurityViewFlipper mSecurityViewFlipper;
private GlobalSettings mGlobalSettings;
@@ -240,6 +245,33 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
}
};
+ private final OnBackAnimationCallback mBackCallback = new OnBackAnimationCallback() {
+ @Override
+ public void onBackCancelled() {
+ // TODO(b/259608500): Remove once back API auto animates progress to 0 on cancel.
+ resetScale();
+ }
+
+ @Override
+ public void onBackInvoked() { }
+
+ @Override
+ public void onBackProgressed(BackEvent event) {
+ float progress = event.getProgress();
+ // TODO(b/263819310): Update the interpolator to match spec.
+ float scale = MIN_BACK_SCALE
+ + (1 - MIN_BACK_SCALE) * (1 - DECELERATE_QUINT.getInterpolation(progress));
+ setScale(scale);
+ }
+ };
+ /**
+ * @return the {@link OnBackAnimationCallback} to animate this view during a back gesture.
+ */
+ @NonNull
+ OnBackAnimationCallback getBackCallback() {
+ return mBackCallback;
+ }
+
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
/**
@@ -736,6 +768,15 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
mViewMode.onDensityOrFontScaleChanged();
}
+ void resetScale() {
+ setScale(1);
+ }
+
+ private void setScale(float scale) {
+ setScaleX(scale);
+ setScaleY(scale);
+ }
+
/**
* Enscapsulates the differences between bouncer modes for the container.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index a72a484fb6f1..57bfe5421049 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -40,7 +40,9 @@ import android.util.Log;
import android.util.Slog;
import android.view.MotionEvent;
import android.view.View;
+import android.window.OnBackAnimationCallback;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
@@ -479,6 +481,9 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
/** Called when the bouncer changes visibility. */
public void onBouncerVisibilityChanged(@View.Visibility int visibility) {
setBouncerVisible(visibility == View.VISIBLE);
+ if (visibility == View.INVISIBLE) {
+ mView.resetScale();
+ }
}
private void setBouncerVisible(boolean visible) {
@@ -588,6 +593,14 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
/**
+ * @return the {@link OnBackAnimationCallback} to animate this view during a back gesture.
+ */
+ @NonNull
+ OnBackAnimationCallback getBackCallback() {
+ return mView.getBackCallback();
+ }
+
+ /**
* Switches to the given security view unless it's already being shown, in which case
* this is a no-op.
*
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d5669b52cf8c..a9695dd62ba4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -63,11 +63,13 @@ import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_RE
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_KEYGUARD_INIT;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_POSTURE_CHANGED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
import android.annotation.AnyThread;
import android.annotation.MainThread;
@@ -84,7 +86,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
@@ -108,7 +109,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
@@ -155,6 +155,7 @@ import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.Assert;
import com.android.systemui.util.settings.SecureSettings;
@@ -271,21 +272,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
"com.android.settings", "com.android.settings.FallbackHome");
- /**
- * If true, the system is in the half-boot-to-decryption-screen state.
- * Prudently disable lockscreen.
- */
- public static final boolean CORE_APPS_ONLY;
-
- static {
- try {
- CORE_APPS_ONLY = IPackageManager.Stub.asInterface(
- ServiceManager.getService("package")).isOnlyCoreApps();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
private final Context mContext;
private final UserTracker mUserTracker;
private final KeyguardUpdateMonitorLogger mLogger;
@@ -357,6 +343,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final TrustManager mTrustManager;
private final UserManager mUserManager;
private final DevicePolicyManager mDevicePolicyManager;
+ private final DevicePostureController mPostureController;
private final BroadcastDispatcher mBroadcastDispatcher;
private final SecureSettings mSecureSettings;
private final InteractionJankMonitor mInteractionJankMonitor;
@@ -374,6 +361,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final FaceManager mFaceManager;
private final LockPatternUtils mLockPatternUtils;
private final boolean mWakeOnFingerprintAcquiredStart;
+ @VisibleForTesting
+ @DevicePostureController.DevicePostureInt
+ protected int mConfigFaceAuthSupportedPosture;
private KeyguardBypassController mKeyguardBypassController;
private List<SubscriptionInfo> mSubscriptionInfo;
@@ -384,6 +374,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private boolean mLogoutEnabled;
private boolean mIsFaceEnrolled;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private int mPostureState = DEVICE_POSTURE_UNKNOWN;
private FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
/**
@@ -1795,6 +1786,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
};
@VisibleForTesting
+ final DevicePostureController.Callback mPostureCallback =
+ new DevicePostureController.Callback() {
+ @Override
+ public void onPostureChanged(int posture) {
+ mPostureState = posture;
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_UPDATED_POSTURE_CHANGED);
+ }
+ };
+
+ @VisibleForTesting
CancellationSignal mFingerprintCancelSignal;
@VisibleForTesting
CancellationSignal mFaceCancelSignal;
@@ -2060,6 +2062,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Nullable FingerprintManager fingerprintManager,
@Nullable BiometricManager biometricManager,
FaceWakeUpTriggersConfig faceWakeUpTriggersConfig,
+ DevicePostureController devicePostureController,
Optional<FingerprintInteractiveToAuthProvider> interactiveToAuthProvider) {
mContext = context;
mSubscriptionManager = subscriptionManager;
@@ -2089,6 +2092,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mDreamManager = dreamManager;
mTelephonyManager = telephonyManager;
mDevicePolicyManager = devicePolicyManager;
+ mPostureController = devicePostureController;
mPackageManager = packageManager;
mFpm = fingerprintManager;
mFaceManager = faceManager;
@@ -2100,6 +2104,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
R.array.config_face_acquire_device_entry_ignorelist))
.boxed()
.collect(Collectors.toSet());
+ mConfigFaceAuthSupportedPosture = mContext.getResources().getInteger(
+ R.integer.config_face_auth_supported_posture);
mFaceWakeUpTriggersConfig = faceWakeUpTriggersConfig;
mHandler = new Handler(mainLooper) {
@@ -2290,6 +2296,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
FACE_AUTH_TRIGGERED_ENROLLMENTS_CHANGED));
}
});
+ if (mConfigFaceAuthSupportedPosture != DEVICE_POSTURE_UNKNOWN) {
+ mPostureController.addCallback(mPostureCallback);
+ }
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_ON_KEYGUARD_INIT);
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
@@ -2734,7 +2743,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
user,
shouldListen,
biometricEnabledForUser,
- mPrimaryBouncerIsOrWillBeShowing,
+ mPrimaryBouncerIsOrWillBeShowing,
userCanSkipBouncer,
mCredentialAttempted,
mDeviceInteractive,
@@ -2794,6 +2803,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
final boolean isUdfpsFingerDown = mAuthController.isUdfpsFingerDown();
+ final boolean isPostureAllowedForFaceAuth =
+ mConfigFaceAuthSupportedPosture == 0 /* DEVICE_POSTURE_UNKNOWN */ ? true
+ : (mPostureState == mConfigFaceAuthSupportedPosture);
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -2810,7 +2822,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& faceAuthAllowedOrDetectionIsNeeded && mIsPrimaryUser
&& (!mSecureCameraLaunched || mOccludingAppRequestingFace)
&& faceAndFpNotAuthenticated
- && !mGoingToSleep;
+ && !mGoingToSleep
+ && isPostureAllowedForFaceAuth;
// Aggregate relevant fields for debug logging.
logListenerModelData(
@@ -2830,6 +2843,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mKeyguardGoingAway,
shouldListenForFaceAssistant,
mOccludingAppRequestingFace,
+ isPostureAllowedForFaceAuth,
mIsPrimaryUser,
mSecureCameraLaunched,
supportsDetect,
@@ -2915,7 +2929,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
getKeyguardSessionId(),
faceAuthUiEvent.getExtraInfo()
);
-
+ mLogger.logFaceUnlockPossible(unlockPossible);
if (unlockPossible) {
mFaceCancelSignal = new CancellationSignal();
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 21d3b24174b6..5b4245595be9 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -132,6 +132,12 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
logBuffer.log(TAG, DEBUG, { int1 = faceRunningState }, { "faceRunningState: $int1" })
}
+ fun logFaceUnlockPossible(isFaceUnlockPossible: Boolean) {
+ logBuffer.log(TAG, DEBUG,
+ { bool1 = isFaceUnlockPossible },
+ {"isUnlockWithFacePossible: $bool1"})
+ }
+
fun logFingerprintAuthForWrongUser(authUserId: Int) {
logBuffer.log(TAG, DEBUG,
{ int1 = authUserId },
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
index 0fc9ef96f6e9..632fcdc16259 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
@@ -22,8 +22,6 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
-import androidx.annotation.Nullable;
-
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dagger.WMComponent;
@@ -55,7 +53,6 @@ public abstract class SystemUIInitializer {
mContext = context;
}
- @Nullable
protected abstract GlobalRootComponent.Builder getGlobalRootComponentBuilder();
/**
@@ -72,11 +69,6 @@ public abstract class SystemUIInitializer {
* Starts the initialization process. This stands up the Dagger graph.
*/
public void init(boolean fromTest) throws ExecutionException, InterruptedException {
- GlobalRootComponent.Builder globalBuilder = getGlobalRootComponentBuilder();
- if (globalBuilder == null) {
- return;
- }
-
mRootComponent = getGlobalRootComponentBuilder()
.context(mContext)
.instrumentationTest(fromTest)
@@ -127,7 +119,6 @@ public abstract class SystemUIInitializer {
.setBackAnimation(Optional.ofNullable(null))
.setDesktopMode(Optional.ofNullable(null));
}
-
mSysUIComponent = builder.build();
if (initializeComponents) {
mSysUIComponent.init();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
index 55c095b0be25..8aa3040c6015 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
@@ -16,7 +16,6 @@
package com.android.systemui
-import android.app.Application
import android.content.Context
import com.android.systemui.dagger.DaggerReferenceGlobalRootComponent
import com.android.systemui.dagger.GlobalRootComponent
@@ -25,17 +24,7 @@ import com.android.systemui.dagger.GlobalRootComponent
* {@link SystemUIInitializer} that stands up AOSP SystemUI.
*/
class SystemUIInitializerImpl(context: Context) : SystemUIInitializer(context) {
-
- override fun getGlobalRootComponentBuilder(): GlobalRootComponent.Builder? {
- return when (Application.getProcessName()) {
- SCREENSHOT_CROSS_PROFILE_PROCESS -> null
- else -> DaggerReferenceGlobalRootComponent.builder()
- }
- }
-
- companion object {
- private const val SYSTEMUI_PROCESS = "com.android.systemui"
- private const val SCREENSHOT_CROSS_PROFILE_PROCESS =
- "$SYSTEMUI_PROCESS:screenshot_cross_profile"
+ override fun getGlobalRootComponentBuilder(): GlobalRootComponent.Builder {
+ return DaggerReferenceGlobalRootComponent.builder()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
index e4c197ff940e..1404053e4618 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
@@ -35,13 +35,13 @@ class AuthBiometricFingerprintAndFaceIconController(
override val actsAsConfirmButton: Boolean = true
- override fun shouldAnimateForTransition(
+ override fun shouldAnimateIconViewForTransition(
@BiometricState oldState: Int,
@BiometricState newState: Int
): Boolean = when (newState) {
STATE_PENDING_CONFIRMATION -> true
STATE_AUTHENTICATED -> false
- else -> super.shouldAnimateForTransition(oldState, newState)
+ else -> super.shouldAnimateIconViewForTransition(oldState, newState)
}
@RawRes
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index b962cc43eddf..436f9dfb0d74 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -104,12 +104,14 @@ open class AuthBiometricFingerprintIconController(
iconView.frame = 0
iconViewOverlay.frame = 0
- if (shouldAnimateForTransition(lastState, newState)) {
+ if (shouldAnimateIconViewForTransition(lastState, newState)) {
iconView.playAnimation()
+ }
+
+ if (shouldAnimateIconViewOverlayForTransition(lastState, newState)) {
iconViewOverlay.playAnimation()
- } else if (lastState == STATE_IDLE && newState == STATE_AUTHENTICATING_ANIMATING_IN) {
- iconView.playAnimation()
}
+
LottieColorUtils.applyDynamicColors(context, iconView)
LottieColorUtils.applyDynamicColors(context, iconViewOverlay)
}
@@ -127,7 +129,7 @@ open class AuthBiometricFingerprintIconController(
}
iconView.frame = 0
- if (shouldAnimateForTransition(lastState, newState)) {
+ if (shouldAnimateIconViewForTransition(lastState, newState)) {
iconView.playAnimation()
}
LottieColorUtils.applyDynamicColors(context, iconView)
@@ -160,7 +162,20 @@ open class AuthBiometricFingerprintIconController(
return if (id != null) context.getString(id) else null
}
- protected open fun shouldAnimateForTransition(
+ protected open fun shouldAnimateIconViewForTransition(
+ @BiometricState oldState: Int,
+ @BiometricState newState: Int
+ ) = when (newState) {
+ STATE_HELP,
+ STATE_ERROR -> true
+ STATE_AUTHENTICATING_ANIMATING_IN,
+ STATE_AUTHENTICATING ->
+ oldState == STATE_ERROR || oldState == STATE_HELP || oldState == STATE_IDLE
+ STATE_AUTHENTICATED -> true
+ else -> false
+ }
+
+ protected open fun shouldAnimateIconViewOverlayForTransition(
@BiometricState oldState: Int,
@BiometricState newState: Int
) = when (newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 1afa9b26ce98..6f594d5eb0e2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -111,7 +111,7 @@ constructor(
context.resources.getInteger(android.R.integer.config_mediumAnimTime).toLong()
private val isReverseDefaultRotation =
- context.getResources().getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)
+ context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)
private var overlayHideAnimator: ViewPropertyAnimator? = null
@@ -268,10 +268,12 @@ constructor(
val isDefaultOrientation =
if (isReverseDefaultRotation) !isNaturalOrientation else isNaturalOrientation
val size = windowManager.maximumWindowMetrics.bounds
+
val displayWidth = if (isDefaultOrientation) size.width() else size.height()
val displayHeight = if (isDefaultOrientation) size.height() else size.width()
val boundsWidth = if (isDefaultOrientation) bounds.width() else bounds.height()
val boundsHeight = if (isDefaultOrientation) bounds.height() else bounds.width()
+
val sensorBounds =
if (overlayOffsets.isYAligned()) {
Rect(
@@ -297,6 +299,7 @@ constructor(
overlayViewParams.x = sensorBounds.left
overlayViewParams.y = sensorBounds.top
+
windowManager.updateViewLayout(overlayView, overlayViewParams)
}
@@ -306,7 +309,12 @@ constructor(
}
// hide after a few seconds if the sensor is oriented down and there are
// large overlapping system bars
- val rotation = context.display?.rotation
+ var rotation = context.display?.rotation
+
+ if (rotation != null) {
+ rotation = getRotationFromDefault(rotation)
+ }
+
if (
windowManager.currentWindowMetrics.windowInsets.hasBigNavigationBar() &&
((rotation == Surface.ROTATION_270 && overlayOffsets.isYAligned()) ||
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index cea1779e39b8..3a9706da9090 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -222,6 +222,10 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("mSensorProps=(" + mSensorProps + ")");
+ pw.println("Using new touch detection framework: " + mFeatureFlags.isEnabled(
+ Flags.UDFPS_NEW_TOUCH_DETECTION));
+ pw.println("Using ellipse touch detection: " + mFeatureFlags.isEnabled(
+ Flags.UDFPS_ELLIPSE_DETECTION));
}
public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
index 583ee3ac8e60..63a1b76b8103 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
@@ -32,7 +32,9 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionListener
@@ -40,8 +42,6 @@ import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
-import com.android.systemui.statusbar.phone.KeyguardBouncer
-import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.KeyguardViewManagerCallback
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.LegacyAlternateBouncer
@@ -112,10 +112,10 @@ constructor(
}
/**
* Hidden amount of input (pin/pattern/password) bouncer. This is used
- * [KeyguardBouncer.EXPANSION_VISIBLE] (0f) to [KeyguardBouncer.EXPANSION_HIDDEN] (1f). Only
- * used for the non-modernBouncer.
+ * [KeyguardBouncerConstants.EXPANSION_VISIBLE] (0f) to
+ * [KeyguardBouncerConstants.EXPANSION_HIDDEN] (1f). Only used for the non-modernBouncer.
*/
- private var inputBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN
+ private var inputBouncerHiddenAmount = KeyguardBouncerConstants.EXPANSION_HIDDEN
private var inputBouncerExpansion = 0f // only used for modernBouncer
private val stateListener: StatusBarStateController.StateListener =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt
index 62bedc627b07..48d48450e13e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt
@@ -26,28 +26,28 @@ data class NormalizedTouchData(
* Value obtained from [MotionEvent.getPointerId], or [MotionEvent.INVALID_POINTER_ID] if the ID
* is not available.
*/
- val pointerId: Int,
+ val pointerId: Int = MotionEvent.INVALID_POINTER_ID,
/** [MotionEvent.getRawX] mapped to natural orientation and native resolution. */
- val x: Float,
+ val x: Float = 0f,
/** [MotionEvent.getRawY] mapped to natural orientation and native resolution. */
- val y: Float,
+ val y: Float = 0f,
/** [MotionEvent.getTouchMinor] mapped to natural orientation and native resolution. */
- val minor: Float,
+ val minor: Float = 0f,
/** [MotionEvent.getTouchMajor] mapped to natural orientation and native resolution. */
- val major: Float,
+ val major: Float = 0f,
/** [MotionEvent.getOrientation] mapped to natural orientation. */
- val orientation: Float,
+ val orientation: Float = 0f,
/** [MotionEvent.getEventTime]. */
- val time: Long,
+ val time: Long = 0,
/** [MotionEvent.getDownTime]. */
- val gestureStart: Long,
+ val gestureStart: Long = 0,
) {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
index 693f64a1f93d..3a01cd502929 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
@@ -43,74 +43,72 @@ class SinglePointerTouchProcessor @Inject constructor(val overlapDetector: Overl
): TouchProcessorResult {
fun preprocess(): PreprocessedTouch {
- // TODO(b/253085297): Add multitouch support. pointerIndex can be > 0 for ACTION_MOVE.
- val pointerIndex = 0
- val touchData = event.normalize(pointerIndex, overlayParams)
- val isGoodOverlap =
- overlapDetector.isGoodOverlap(touchData, overlayParams.nativeSensorBounds)
- return PreprocessedTouch(touchData, previousPointerOnSensorId, isGoodOverlap)
+ val touchData = List(event.pointerCount) { event.normalize(it, overlayParams) }
+ val pointersOnSensor =
+ touchData
+ .filter { overlapDetector.isGoodOverlap(it, overlayParams.nativeSensorBounds) }
+ .map { it.pointerId }
+ return PreprocessedTouch(touchData, previousPointerOnSensorId, pointersOnSensor)
}
return when (event.actionMasked) {
- MotionEvent.ACTION_DOWN -> processActionDown(preprocess())
+ MotionEvent.ACTION_DOWN,
+ MotionEvent.ACTION_POINTER_DOWN,
MotionEvent.ACTION_MOVE -> processActionMove(preprocess())
- MotionEvent.ACTION_UP -> processActionUp(preprocess())
- MotionEvent.ACTION_CANCEL ->
- processActionCancel(event.normalize(pointerIndex = 0, overlayParams))
+ MotionEvent.ACTION_UP,
+ MotionEvent.ACTION_POINTER_UP ->
+ processActionUp(preprocess(), event.getPointerId(event.actionIndex))
+ MotionEvent.ACTION_CANCEL -> processActionCancel(NormalizedTouchData())
else ->
Failure("Unsupported MotionEvent." + MotionEvent.actionToString(event.actionMasked))
}
}
}
+/**
+ * [data] contains a list of NormalizedTouchData for pointers in the motionEvent ordered by
+ * pointerIndex
+ *
+ * [previousPointerOnSensorId] the pointerId of the previous pointer on the sensor,
+ * [MotionEvent.INVALID_POINTER_ID] if none
+ *
+ * [pointersOnSensor] contains a list of ids of pointers on the sensor
+ */
private data class PreprocessedTouch(
- val data: NormalizedTouchData,
+ val data: List<NormalizedTouchData>,
val previousPointerOnSensorId: Int,
- val isGoodOverlap: Boolean,
+ val pointersOnSensor: List<Int>,
)
-private fun processActionDown(touch: PreprocessedTouch): TouchProcessorResult {
- return if (touch.isGoodOverlap) {
- ProcessedTouch(InteractionEvent.DOWN, pointerOnSensorId = touch.data.pointerId, touch.data)
- } else {
- val event =
- if (touch.data.pointerId == touch.previousPointerOnSensorId) {
- InteractionEvent.UP
- } else {
- InteractionEvent.UNCHANGED
- }
- ProcessedTouch(event, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
- }
-}
-
private fun processActionMove(touch: PreprocessedTouch): TouchProcessorResult {
val hadPointerOnSensor = touch.previousPointerOnSensorId != INVALID_POINTER_ID
- val interactionEvent =
- when {
- touch.isGoodOverlap && !hadPointerOnSensor -> InteractionEvent.DOWN
- !touch.isGoodOverlap && hadPointerOnSensor -> InteractionEvent.UP
- else -> InteractionEvent.UNCHANGED
- }
- val pointerOnSensorId =
- when (interactionEvent) {
- InteractionEvent.UNCHANGED -> touch.previousPointerOnSensorId
- InteractionEvent.DOWN -> touch.data.pointerId
- else -> INVALID_POINTER_ID
- }
- return ProcessedTouch(interactionEvent, pointerOnSensorId, touch.data)
+ val hasPointerOnSensor = touch.pointersOnSensor.isNotEmpty()
+ val pointerOnSensorId = touch.pointersOnSensor.firstOrNull() ?: INVALID_POINTER_ID
+
+ return if (!hadPointerOnSensor && hasPointerOnSensor) {
+ val data = touch.data.find { it.pointerId == pointerOnSensorId } ?: NormalizedTouchData()
+ ProcessedTouch(InteractionEvent.DOWN, data.pointerId, data)
+ } else if (hadPointerOnSensor && !hasPointerOnSensor) {
+ ProcessedTouch(InteractionEvent.UP, INVALID_POINTER_ID, NormalizedTouchData())
+ } else {
+ val data = touch.data.find { it.pointerId == pointerOnSensorId } ?: NormalizedTouchData()
+ ProcessedTouch(InteractionEvent.UNCHANGED, pointerOnSensorId, data)
+ }
}
-private fun processActionUp(touch: PreprocessedTouch): TouchProcessorResult {
- return if (touch.isGoodOverlap) {
- ProcessedTouch(InteractionEvent.UP, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
+private fun processActionUp(touch: PreprocessedTouch, actionId: Int): TouchProcessorResult {
+ // Finger lifted and it was the only finger on the sensor
+ return if (touch.pointersOnSensor.size == 1 && touch.pointersOnSensor.contains(actionId)) {
+ ProcessedTouch(
+ InteractionEvent.UP,
+ pointerOnSensorId = INVALID_POINTER_ID,
+ NormalizedTouchData()
+ )
} else {
- val event =
- if (touch.previousPointerOnSensorId != INVALID_POINTER_ID) {
- InteractionEvent.UP
- } else {
- InteractionEvent.UNCHANGED
- }
- ProcessedTouch(event, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
+ // Pick new pointerOnSensor that's not the finger that was lifted
+ val pointerOnSensorId = touch.pointersOnSensor.find { it != actionId } ?: INVALID_POINTER_ID
+ val data = touch.data.find { it.pointerId == pointerOnSensorId } ?: NormalizedTouchData()
+ ProcessedTouch(InteractionEvent.UNCHANGED, data.pointerId, data)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
index f95a8ee89a2c..7bbfec7df9d8 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
@@ -28,7 +28,6 @@ class LaunchableImageView : ImageView, LaunchableView {
LaunchableViewDelegate(
this,
superSetVisibility = { super.setVisibility(it) },
- superSetTransitionVisibility = { super.setTransitionVisibility(it) },
)
constructor(context: Context?) : super(context)
@@ -53,8 +52,4 @@ class LaunchableImageView : ImageView, LaunchableView {
override fun setVisibility(visibility: Int) {
delegate.setVisibility(visibility)
}
-
- override fun setTransitionVisibility(visibility: Int) {
- delegate.setTransitionVisibility(visibility)
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt
index c27b82aeeb47..ddde6280f3a2 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt
@@ -28,7 +28,6 @@ class LaunchableLinearLayout : LinearLayout, LaunchableView {
LaunchableViewDelegate(
this,
superSetVisibility = { super.setVisibility(it) },
- superSetTransitionVisibility = { super.setTransitionVisibility(it) },
)
constructor(context: Context?) : super(context)
@@ -53,8 +52,4 @@ class LaunchableLinearLayout : LinearLayout, LaunchableView {
override fun setVisibility(visibility: Int) {
delegate.setVisibility(visibility)
}
-
- override fun setTransitionVisibility(visibility: Int) {
- delegate.setTransitionVisibility(visibility)
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
index e5ec727f0437..c0f854958c41 100644
--- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
+++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
@@ -17,8 +17,12 @@
package com.android.systemui.compose
+import android.content.Context
+import android.view.View
import androidx.activity.ComponentActivity
+import androidx.lifecycle.LifecycleOwner
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
/**
* A facade to interact with Compose, when it is available.
@@ -35,10 +39,22 @@ interface BaseComposeFacade {
*/
fun isComposeAvailable(): Boolean
+ /**
+ * Return the [ComposeInitializer] to make Compose usable in windows outside normal activities.
+ */
+ fun composeInitializer(): ComposeInitializer
+
/** Bind the content of [activity] to [viewModel]. */
fun setPeopleSpaceActivityContent(
activity: ComponentActivity,
viewModel: PeopleViewModel,
onResult: (PeopleViewModel.Result) -> Unit,
)
+
+ /** Create a [View] to represent [viewModel] on screen. */
+ fun createFooterActionsView(
+ context: Context,
+ viewModel: FooterActionsViewModel,
+ qsVisibilityLifecycleOwner: LifecycleOwner,
+ ): View
}
diff --git a/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt b/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt
new file mode 100644
index 000000000000..90dc3a00daa2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.compose
+
+import android.view.View
+
+/**
+ * An initializer to use Compose outside of an Activity, e.g. inside a window added directly using
+ * [android.view.WindowManager.addView] (like the shade or status bar) or inside a dialog.
+ *
+ * Example:
+ * ```
+ * windowManager.addView(MyWindowRootView(context), /* layoutParams */)
+ *
+ * class MyWindowRootView(context: Context) : FrameLayout(context) {
+ * override fun onAttachedToWindow() {
+ * super.onAttachedToWindow()
+ * ComposeInitializer.onAttachedToWindow(this)
+ * }
+ *
+ * override fun onDetachedFromWindow() {
+ * super.onDetachedFromWindow()
+ * ComposeInitializer.onDetachedFromWindow(this)
+ * }
+ * }
+ * ```
+ */
+interface ComposeInitializer {
+ /** Function to be called on your window root view's [View.onAttachedToWindow] function. */
+ fun onAttachedToWindow(root: View)
+
+ /** Function to be called on your window root view's [View.onDetachedFromWindow] function. */
+ fun onDetachedFromWindow(root: View)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
index eed55315e836..9b2a224f17e0 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
@@ -51,13 +51,22 @@ interface ControlsBindingController : UserAwareController {
fun bindAndLoadSuggested(component: ComponentName, callback: LoadCallback)
/**
- * Request to bind to the given service.
+ * Request to bind to the given service. This should only be used for services using the full
+ * [ControlsProviderService] API, where SystemUI renders the devices' UI.
*
* @param component The [ComponentName] of the service to bind
*/
fun bindService(component: ComponentName)
/**
+ * Bind to a service that provides a Device Controls panel (embedded activity). This will allow
+ * the app to remain "warm", and reduce latency.
+ *
+ * @param component The [ComponentName] of the [ControlsProviderService] to bind.
+ */
+ fun bindServiceForPanel(component: ComponentName)
+
+ /**
* Send a subscribe message to retrieve status of a set of controls.
*
* @param structureInfo structure containing the controls to update
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index 2f0fd99337e5..3d6d3356fb55 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -170,6 +170,10 @@ open class ControlsBindingControllerImpl @Inject constructor(
retrieveLifecycleManager(component).bindService()
}
+ override fun bindServiceForPanel(component: ComponentName) {
+ retrieveLifecycleManager(component).bindServiceForPanel()
+ }
+
override fun changeUser(newUser: UserHandle) {
if (newUser == currentUser) return
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 2f49c3fe863e..f29f6d0dd0cb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -189,6 +189,14 @@ interface ControlsController : UserAwareController {
fun getPreferredSelection(): SelectedItem
/**
+ * Bind to a service that provides a Device Controls panel (embedded activity). This will allow
+ * the app to remain "warm", and reduce latency.
+ *
+ * @param component The [ComponentName] of the [ControlsProviderService] to bind.
+ */
+ fun bindComponentForPanel(componentName: ComponentName)
+
+ /**
* Interface for structure to pass data to [ControlsFavoritingActivity].
*/
interface LoadData {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 80c5f661f9a3..111fcbbe30be 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -477,6 +477,10 @@ class ControlsControllerImpl @Inject constructor (
bindingController.unsubscribe()
}
+ override fun bindComponentForPanel(componentName: ComponentName) {
+ bindingController.bindServiceForPanel(componentName)
+ }
+
override fun addFavorite(
componentName: ComponentName,
structureName: CharSequence,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index 5b38e5b28be9..72c3a943c30b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -78,6 +78,10 @@ class ControlsProviderLifecycleManager(
private const val DEBUG = true
private val BIND_FLAGS = Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE or
Context.BIND_NOT_PERCEPTIBLE
+ // Use BIND_NOT_PERCEPTIBLE so it will be at lower priority from SystemUI.
+ // However, don't use WAIVE_PRIORITY, as by itself, it will kill the app
+ // once the Task is finished in the device controls panel.
+ private val BIND_FLAGS_PANEL = Context.BIND_AUTO_CREATE or Context.BIND_NOT_PERCEPTIBLE
}
private val intent = Intent().apply {
@@ -87,18 +91,19 @@ class ControlsProviderLifecycleManager(
})
}
- private fun bindService(bind: Boolean) {
+ private fun bindService(bind: Boolean, forPanel: Boolean = false) {
executor.execute {
requiresBound = bind
if (bind) {
- if (bindTryCount != MAX_BIND_RETRIES) {
+ if (bindTryCount != MAX_BIND_RETRIES && wrapper == null) {
if (DEBUG) {
Log.d(TAG, "Binding service $intent")
}
bindTryCount++
try {
+ val flags = if (forPanel) BIND_FLAGS_PANEL else BIND_FLAGS
val bound = context
- .bindServiceAsUser(intent, serviceConnection, BIND_FLAGS, user)
+ .bindServiceAsUser(intent, serviceConnection, flags, user)
if (!bound) {
context.unbindService(serviceConnection)
}
@@ -279,6 +284,10 @@ class ControlsProviderLifecycleManager(
bindService(true)
}
+ fun bindServiceForPanel() {
+ bindService(bind = true, forPanel = true)
+ }
+
/**
* Request unbind from the service.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 1e3e5cd1c31c..6289788f650a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -232,6 +232,8 @@ class ControlsUiControllerImpl @Inject constructor (
ControlKey(selected.structure.componentName, it.ci.controlId)
}
controlsController.get().subscribeToFavorites(selected.structure)
+ } else {
+ controlsController.get().bindComponentForPanel(selected.componentName)
}
listingCallback = createCallback(::showControlsView)
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 8012dea643fb..8e9992fdd296 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -31,6 +31,7 @@ import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.keyboard.KeyboardUI
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.log.SessionTracker
+import com.android.systemui.media.dialog.MediaOutputSwitcherDialogUI
import com.android.systemui.media.RingtonePlayer
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
@@ -217,6 +218,12 @@ abstract class SystemUICoreStartableModule {
@ClassKey(ToastUI::class)
abstract fun bindToastUI(service: ToastUI): CoreStartable
+ /** Inject into MediaOutputSwitcherDialogUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(MediaOutputSwitcherDialogUI::class)
+ abstract fun MediaOutputSwitcherDialogUI(sysui: MediaOutputSwitcherDialogUI): CoreStartable
+
/** Inject into VolumeUI. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 6d13740afe8b..2d0dfa1d921b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -54,6 +54,7 @@ import com.android.systemui.motiontool.MotionToolModule;
import com.android.systemui.navigationbar.NavigationBarComponent;
import com.android.systemui.notetask.NoteTaskModule;
import com.android.systemui.people.PeopleModule;
+import com.android.systemui.plugins.BcSmartspaceConfigPlugin;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import com.android.systemui.privacy.PrivacyModule;
import com.android.systemui.qs.FgsManagerController;
@@ -211,6 +212,9 @@ public abstract class SystemUIModule {
abstract BcSmartspaceDataPlugin optionalBcSmartspaceDataPlugin();
@BindsOptionalOf
+ abstract BcSmartspaceConfigPlugin optionalBcSmartspaceConfigPlugin();
+
+ @BindsOptionalOf
abstract Recents optionalRecents();
@BindsOptionalOf
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 37183e89df29..2ef1262fe55e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -116,6 +116,7 @@ public class DozeSensors {
private boolean mListening;
private boolean mListeningTouchScreenSensors;
private boolean mListeningProxSensors;
+ private boolean mListeningAodOnlySensors;
private boolean mUdfpsEnrolled;
@DevicePostureController.DevicePostureInt
@@ -184,7 +185,8 @@ public class DozeSensors {
dozeParameters.getPulseOnSigMotion(),
DozeLog.PULSE_REASON_SENSOR_SIGMOTION,
false /* touchCoords */,
- false /* touchscreen */),
+ false /* touchscreen */
+ ),
new TriggerSensor(
mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),
Settings.Secure.DOZE_PICK_UP_GESTURE,
@@ -195,14 +197,17 @@ public class DozeSensors {
false /* touchscreen */,
false /* ignoresSetting */,
false /* requires prox */,
- true /* immediatelyReRegister */),
+ true /* immediatelyReRegister */,
+ false /* requiresAod */
+ ),
new TriggerSensor(
findSensor(config.doubleTapSensorType()),
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
true /* configured */,
DozeLog.REASON_SENSOR_DOUBLE_TAP,
dozeParameters.doubleTapReportsTouchCoordinates(),
- true /* touchscreen */),
+ true /* touchscreen */
+ ),
new TriggerSensor(
findSensors(config.tapSensorTypeMapping()),
Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
@@ -214,7 +219,9 @@ public class DozeSensors {
false /* ignoresSetting */,
dozeParameters.singleTapUsesProx(mDevicePosture) /* requiresProx */,
true /* immediatelyReRegister */,
- mDevicePosture),
+ mDevicePosture,
+ false
+ ),
new TriggerSensor(
findSensor(config.longPressSensorType()),
Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
@@ -225,7 +232,9 @@ public class DozeSensors {
true /* touchscreen */,
false /* ignoresSetting */,
dozeParameters.longPressUsesProx() /* requiresProx */,
- true /* immediatelyReRegister */),
+ true /* immediatelyReRegister */,
+ false /* requiresAod */
+ ),
new TriggerSensor(
findSensor(config.udfpsLongPressSensorType()),
"doze_pulse_on_auth",
@@ -236,7 +245,9 @@ public class DozeSensors {
true /* touchscreen */,
false /* ignoresSetting */,
dozeParameters.longPressUsesProx(),
- false /* immediatelyReRegister */),
+ false /* immediatelyReRegister */,
+ true /* requiresAod */
+ ),
new PluginSensor(
new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
@@ -244,7 +255,8 @@ public class DozeSensors {
&& mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT),
DozeLog.REASON_SENSOR_WAKE_UP_PRESENCE,
false /* reports touch coordinates */,
- false /* touchscreen */),
+ false /* touchscreen */
+ ),
new PluginSensor(
new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN),
Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
@@ -252,7 +264,8 @@ public class DozeSensors {
DozeLog.PULSE_REASON_SENSOR_WAKE_REACH,
false /* reports touch coordinates */,
false /* touchscreen */,
- mConfig.getWakeLockScreenDebounce()),
+ mConfig.getWakeLockScreenDebounce()
+ ),
new TriggerSensor(
findSensor(config.quickPickupSensorType()),
Settings.Secure.DOZE_QUICK_PICKUP_GESTURE,
@@ -263,7 +276,9 @@ public class DozeSensors {
false /* requiresTouchscreen */,
false /* ignoresSetting */,
false /* requiresProx */,
- true /* immediatelyReRegister */),
+ true /* immediatelyReRegister */,
+ false /* requiresAod */
+ ),
};
setProxListening(false); // Don't immediately start listening when we register.
mProximitySensor.register(
@@ -357,29 +372,36 @@ public class DozeSensors {
/**
* If sensors should be registered and sending signals.
*/
- public void setListening(boolean listen, boolean includeTouchScreenSensors) {
- if (mListening == listen && mListeningTouchScreenSensors == includeTouchScreenSensors) {
+ public void setListening(boolean listen, boolean includeTouchScreenSensors,
+ boolean includeAodOnlySensors) {
+ if (mListening == listen && mListeningTouchScreenSensors == includeTouchScreenSensors
+ && mListeningAodOnlySensors == includeAodOnlySensors) {
return;
}
mListening = listen;
mListeningTouchScreenSensors = includeTouchScreenSensors;
+ mListeningAodOnlySensors = includeAodOnlySensors;
updateListening();
}
/**
* If sensors should be registered and sending signals.
*/
- public void setListening(boolean listen, boolean includeTouchScreenSensors,
- boolean lowPowerStateOrOff) {
+ public void setListeningWithPowerState(boolean listen, boolean includeTouchScreenSensors,
+ boolean includeAodRequiringSensors, boolean lowPowerStateOrOff) {
final boolean shouldRegisterProxSensors =
!mSelectivelyRegisterProxSensors || lowPowerStateOrOff;
- if (mListening == listen && mListeningTouchScreenSensors == includeTouchScreenSensors
- && mListeningProxSensors == shouldRegisterProxSensors) {
+ if (mListening == listen
+ && mListeningTouchScreenSensors == includeTouchScreenSensors
+ && mListeningProxSensors == shouldRegisterProxSensors
+ && mListeningAodOnlySensors == includeAodRequiringSensors
+ ) {
return;
}
mListening = listen;
mListeningTouchScreenSensors = includeTouchScreenSensors;
mListeningProxSensors = shouldRegisterProxSensors;
+ mListeningAodOnlySensors = includeAodRequiringSensors;
updateListening();
}
@@ -391,7 +413,8 @@ public class DozeSensors {
for (TriggerSensor s : mTriggerSensors) {
boolean listen = mListening
&& (!s.mRequiresTouchscreen || mListeningTouchScreenSensors)
- && (!s.mRequiresProx || mListeningProxSensors);
+ && (!s.mRequiresProx || mListeningProxSensors)
+ && (!s.mRequiresAod || mListeningAodOnlySensors);
s.setListening(listen);
if (listen) {
anyListening = true;
@@ -499,6 +522,9 @@ public class DozeSensors {
private final boolean mRequiresTouchscreen;
private final boolean mRequiresProx;
+ // Whether the sensor should only register if the device is in AOD
+ private final boolean mRequiresAod;
+
// Whether to immediately re-register this sensor after the sensor is triggered.
// If false, the sensor registration will be updated on the next AOD state transition.
private final boolean mImmediatelyReRegister;
@@ -527,7 +553,8 @@ public class DozeSensors {
requiresTouchscreen,
false /* ignoresSetting */,
false /* requiresProx */,
- true /* immediatelyReRegister */
+ true /* immediatelyReRegister */,
+ false
);
}
@@ -541,7 +568,8 @@ public class DozeSensors {
boolean requiresTouchscreen,
boolean ignoresSetting,
boolean requiresProx,
- boolean immediatelyReRegister
+ boolean immediatelyReRegister,
+ boolean requiresAod
) {
this(
new Sensor[]{ sensor },
@@ -554,7 +582,8 @@ public class DozeSensors {
ignoresSetting,
requiresProx,
immediatelyReRegister,
- DevicePostureController.DEVICE_POSTURE_UNKNOWN
+ DevicePostureController.DEVICE_POSTURE_UNKNOWN,
+ requiresAod
);
}
@@ -569,7 +598,8 @@ public class DozeSensors {
boolean ignoresSetting,
boolean requiresProx,
boolean immediatelyReRegister,
- @DevicePostureController.DevicePostureInt int posture
+ @DevicePostureController.DevicePostureInt int posture,
+ boolean requiresAod
) {
mSensors = sensors;
mSetting = setting;
@@ -580,6 +610,7 @@ public class DozeSensors {
mRequiresTouchscreen = requiresTouchscreen;
mIgnoresSetting = ignoresSetting;
mRequiresProx = requiresProx;
+ mRequiresAod = requiresAod;
mPosture = posture;
mImmediatelyReRegister = immediatelyReRegister;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index b95c3f3c0ee7..b70960832d32 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -111,6 +111,7 @@ public class DozeTriggers implements DozeMachine.Part {
private boolean mWantProxSensor;
private boolean mWantTouchScreenSensors;
private boolean mWantSensors;
+ private boolean mInAod;
private final UserTracker.Callback mUserChangedCallback =
new UserTracker.Callback() {
@@ -460,12 +461,19 @@ public class DozeTriggers implements DozeMachine.Part {
mDozeSensors.requestTemporaryDisable();
break;
case DOZE:
+ mAodInterruptRunnable = null;
+ mWantProxSensor = false;
+ mWantSensors = true;
+ mWantTouchScreenSensors = true;
+ mInAod = false;
+ break;
case DOZE_AOD:
mAodInterruptRunnable = null;
- mWantProxSensor = newState != DozeMachine.State.DOZE;
+ mWantProxSensor = true;
mWantSensors = true;
mWantTouchScreenSensors = true;
- if (newState == DozeMachine.State.DOZE_AOD && !sWakeDisplaySensorState) {
+ mInAod = true;
+ if (!sWakeDisplaySensorState) {
onWakeScreen(false, newState, DozeLog.REASON_SENSOR_WAKE_UP_PRESENCE);
}
break;
@@ -491,7 +499,7 @@ public class DozeTriggers implements DozeMachine.Part {
break;
default:
}
- mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors);
+ mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors, mInAod);
}
private void registerCallbacks() {
@@ -510,11 +518,12 @@ public class DozeTriggers implements DozeMachine.Part {
private void stopListeningToAllTriggers() {
unregisterCallbacks();
- mDozeSensors.setListening(false, false);
+ mDozeSensors.setListening(false, false, false);
mDozeSensors.setProxListening(false);
mWantSensors = false;
mWantProxSensor = false;
mWantTouchScreenSensors = false;
+ mInAod = false;
}
@Override
@@ -523,7 +532,8 @@ public class DozeTriggers implements DozeMachine.Part {
final boolean lowPowerStateOrOff = state == Display.STATE_DOZE
|| state == Display.STATE_DOZE_SUSPEND || state == Display.STATE_OFF;
mDozeSensors.setProxListening(mWantProxSensor && lowPowerStateOrOff);
- mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors, lowPowerStateOrOff);
+ mDozeSensors.setListeningWithPowerState(mWantSensors, mWantTouchScreenSensors,
+ mInAod, lowPowerStateOrOff);
if (mAodInterruptRunnable != null && state == Display.STATE_ON) {
mAodInterruptRunnable.run();
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 3106173d81b2..33c8379d2e5c 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -38,6 +38,7 @@ import com.android.systemui.dreams.complication.ComplicationHostViewController;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.dagger.DreamOverlayModule;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -85,8 +86,9 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
private boolean mBouncerAnimating;
- private final KeyguardBouncer.PrimaryBouncerExpansionCallback mBouncerExpansionCallback =
- new KeyguardBouncer.PrimaryBouncerExpansionCallback() {
+ private final PrimaryBouncerExpansionCallback
+ mBouncerExpansionCallback =
+ new PrimaryBouncerExpansionCallback() {
@Override
public void onStartingToShow() {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index 92cdcf99f013..44207f4aecf5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -36,10 +36,10 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -274,16 +274,18 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
(float) Math.hypot(horizontalVelocity, verticalVelocity);
final float expansion = flingRevealsOverlay(verticalVelocity, velocityVector)
- ? KeyguardBouncer.EXPANSION_HIDDEN : KeyguardBouncer.EXPANSION_VISIBLE;
+ ? KeyguardBouncerConstants.EXPANSION_HIDDEN
+ : KeyguardBouncerConstants.EXPANSION_VISIBLE;
// Log the swiping up to show Bouncer event.
- if (!mBouncerInitiallyShowing && expansion == KeyguardBouncer.EXPANSION_VISIBLE) {
+ if (!mBouncerInitiallyShowing
+ && expansion == KeyguardBouncerConstants.EXPANSION_VISIBLE) {
mUiEventLogger.log(DreamEvent.DREAM_SWIPED);
}
flingToExpansion(verticalVelocity, expansion);
- if (expansion == KeyguardBouncer.EXPANSION_HIDDEN) {
+ if (expansion == KeyguardBouncerConstants.EXPANSION_HIDDEN) {
mStatusBarKeyguardViewManager.reset(false);
}
break;
@@ -302,7 +304,8 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
float dragDownAmount = expansionFraction * expansionHeight;
setPanelExpansion(expansionFraction, dragDownAmount);
});
- if (!mBouncerInitiallyShowing && targetExpansion == KeyguardBouncer.EXPANSION_VISIBLE) {
+ if (!mBouncerInitiallyShowing
+ && targetExpansion == KeyguardBouncerConstants.EXPANSION_VISIBLE) {
animator.addListener(
new AnimatorListenerAdapter() {
@Override
@@ -335,7 +338,7 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
final float targetHeight = viewHeight * expansion;
final float expansionHeight = targetHeight - currentHeight;
final ValueAnimator animator = createExpansionAnimator(expansion, expansionHeight);
- if (expansion == KeyguardBouncer.EXPANSION_HIDDEN) {
+ if (expansion == KeyguardBouncerConstants.EXPANSION_HIDDEN) {
// Hides the bouncer, i.e., fully expands the space above the bouncer.
mFlingAnimationUtilsClosing.apply(animator, currentHeight, targetHeight, velocity,
viewHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index c9a2a0b9b7e9..f0416e595eba 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -75,15 +75,7 @@ object Flags {
unreleasedFlag(119, "notification_memory_logging_enabled", teamfood = true)
// TODO(b/254512731): Tracking Bug
- @JvmField
- val NOTIFICATION_DISMISSAL_FADE =
- unreleasedFlag(113, "notification_dismissal_fade", teamfood = true)
-
- // TODO(b/259558771): Tracking Bug
- val STABILITY_INDEX_FIX = releasedFlag(114, "stability_index_fix")
-
- // TODO(b/259559750): Tracking Bug
- val SEMI_STABLE_SORT = releasedFlag(115, "semi_stable_sort")
+ @JvmField val NOTIFICATION_DISMISSAL_FADE = releasedFlag(113, "notification_dismissal_fade")
@JvmField val USE_ROUNDNESS_SOURCETYPES = releasedFlag(116, "use_roundness_sourcetype")
@@ -110,6 +102,10 @@ object Flags {
val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
unreleasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
+ // TODO(b/263414400): Tracking Bug
+ @JvmField
+ val NOTIFICATION_ANIMATE_BIG_PICTURE = unreleasedFlag(120, "notification_animate_big_picture")
+
// 200 - keyguard/lockscreen
// ** Flag retired **
// public static final BooleanFlag KEYGUARD_LAYOUT =
@@ -183,7 +179,7 @@ object Flags {
* of the Alternate/Authentication Bouncer. No visual UI changes.
*/
// TODO(b/260619425): Tracking Bug
- @JvmField val MODERN_ALTERNATE_BOUNCER = unreleasedFlag(219, "modern_alternate_bouncer")
+ @JvmField val MODERN_ALTERNATE_BOUNCER = releasedFlag(219, "modern_alternate_bouncer")
/** Flag to control the migration of face auth to modern architecture. */
// TODO(b/262838215): Tracking bug
@@ -200,7 +196,7 @@ object Flags {
/** A different path for unocclusion transitions back to keyguard */
// TODO(b/262859270): Tracking Bug
@JvmField
- val UNOCCLUSION_TRANSITION = unreleasedFlag(223, "unocclusion_transition", teamfood = false)
+ val UNOCCLUSION_TRANSITION = unreleasedFlag(223, "unocclusion_transition", teamfood = true)
// flag for controlling auto pin confirmation and material u shapes in bouncer
@JvmField
@@ -210,6 +206,12 @@ object Flags {
// TODO(b/262859270): Tracking Bug
@JvmField val FALSING_OFF_FOR_UNFOLDED = releasedFlag(225, "falsing_off_for_unfolded")
+ /** Enables code to show contextual loyalty cards in wallet entrypoints */
+ // TODO(b/247587924): Tracking Bug
+ @JvmField
+ val ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS =
+ unreleasedFlag(226, "enable_wallet_contextual_loyalty_cards", teamfood = false)
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -293,7 +295,7 @@ object Flags {
// 801 - region sampling
// TODO(b/254512848): Tracking Bug
- val REGION_SAMPLING = unreleasedFlag(801, "region_sampling", teamfood = true)
+ val REGION_SAMPLING = unreleasedFlag(801, "region_sampling")
// 803 - screen contents translation
// TODO(b/254513187): Tracking Bug
@@ -336,6 +338,9 @@ object Flags {
val MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE =
unreleasedFlag(910, "media_ttt_receiver_success_ripple", teamfood = true)
+ // TODO(b/263512203): Tracking Bug
+ val MEDIA_EXPLICIT_INDICATOR = unreleasedFlag(911, "media_explicit_indicator", teamfood = true)
+
// 1000 - dock
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
@@ -406,6 +411,17 @@ object Flags {
val WM_DESKTOP_WINDOWING_2 =
sysPropBooleanFlag(1112, "persist.wm.debug.desktop_mode_2", default = false)
+ // TODO(b/254513207): Tracking Bug to delete
+ @Keep
+ @JvmField
+ val WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES =
+ unreleasedFlag(
+ 1113,
+ name = "screen_record_enterprise_policies",
+ namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ teamfood = false
+ )
+
// 1200 - predictive back
@Keep
@JvmField
@@ -469,8 +485,7 @@ object Flags {
// 1800 - shade container
@JvmField
- val LEAVE_SHADE_OPEN_FOR_BUGREPORT =
- unreleasedFlag(1800, "leave_shade_open_for_bugreport", teamfood = true)
+ val LEAVE_SHADE_OPEN_FOR_BUGREPORT = releasedFlag(1800, "leave_shade_open_for_bugreport")
// 1900
@JvmField val NOTE_TASKS = unreleasedFlag(1900, "keycode_flag")
@@ -496,6 +511,7 @@ object Flags {
@JvmField val ENABLE_STYLUS_CHARGING_UI = unreleasedFlag(2301, "enable_stylus_charging_ui")
@JvmField
val ENABLE_USI_BATTERY_NOTIFICATIONS = unreleasedFlag(2302, "enable_usi_battery_notifications")
+ @JvmField val ENABLE_STYLUS_EDUCATION = unreleasedFlag(2303, "enable_stylus_education")
// 2400 - performance tools and debugging info
// TODO(b/238923086): Tracking Bug
@@ -513,6 +529,23 @@ object Flags {
@JvmField
val OUTPUT_SWITCHER_DEVICE_STATUS = unreleasedFlag(2502, "output_switcher_device_status")
+ // TODO(b/20911786): Tracking Bug
+ @JvmField
+ val OUTPUT_SWITCHER_SHOW_API_ENABLED =
+ unreleasedFlag(2503, "output_switcher_show_api_enabled", teamfood = true)
+
+ // 2600 - unfold transitions
+ // TODO(b/265764985): Tracking Bug
+ @Keep
+ @JvmField
+ val ENABLE_DARK_VIGNETTE_WHEN_FOLDING =
+ unreleasedFlag(2600, "enable_dark_vignette_when_folding")
+
// TODO(b259590361): Tracking bug
val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release")
+
+ // 2600 - keyboard shortcut
+ // TODO(b/259352579): Tracking Bug
+ @JvmField
+ val SHORTCUT_LIST_SEARCH_LAYOUT = unreleasedFlag(2600, "shortcut_list_search_layout")
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index eaf1081a374a..482138e6c277 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -282,6 +282,7 @@ class CustomizationProvider :
.ENABLEMENT_ACTION_TEXT,
Contract.LockScreenQuickAffordances.AffordanceTable.Columns
.ENABLEMENT_COMPONENT_NAME,
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns.CONFIGURE_INTENT,
)
)
.apply {
@@ -298,6 +299,7 @@ class CustomizationProvider :
),
representation.actionText,
representation.actionComponentName,
+ representation.configureIntent?.toUri(0),
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8200f259a992..9756cadff8cd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1912,12 +1912,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
* Enable the keyguard if the settings are appropriate.
*/
private void doKeyguardLocked(Bundle options) {
- if (KeyguardUpdateMonitor.CORE_APPS_ONLY) {
- // Don't show keyguard during half-booted cryptkeeper stage.
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because booting to cryptkeeper");
- return;
- }
-
// if another app is disabling us, don't show
if (!mExternallyEnabled) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
@@ -1926,13 +1920,23 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
return;
}
- // if the keyguard is already showing, don't bother. check flags in both files
- // to account for the hiding animation which results in a delay and discrepancy
- // between flags
+ // If the keyguard is already showing, see if we don't need to bother re-showing it. Check
+ // flags in both files to account for the hiding animation which results in a delay and
+ // discrepancy between flags.
if (mShowing && mKeyguardStateController.isShowing()) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
- resetStateLocked();
- return;
+ if (mPM.isInteractive()) {
+ // It's already showing, and we're not trying to show it while the screen is off.
+ // We can simply reset all of the views.
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
+ resetStateLocked();
+ return;
+ } else {
+ // We are trying to show the keyguard while the screen is off - this results from
+ // race conditions involving locking while unlocking. Don't short-circuit here and
+ // ensure the keyguard is fully re-shown.
+ Log.e(TAG,
+ "doKeyguard: already showing, but re-showing since we're not interactive");
+ }
}
// In split system user mode, we never unlock system user.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt
index 80c6130955c5..faeb48526ae4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.data
import android.view.KeyEvent
+import android.window.OnBackAnimationCallback
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter
import java.lang.ref.WeakReference
@@ -51,4 +52,6 @@ interface BouncerViewDelegate {
cancelAction: Runnable?,
)
fun willDismissWithActions(): Boolean
+ /** @return the {@link OnBackAnimationCallback} to animate Bouncer during a back gesture. */
+ fun getBackCallback(): OnBackAnimationCallback
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
index 8efb36624831..ed1ff329004a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.data.quickaffordance
import android.content.Context
+import android.content.Intent
import android.net.Uri
import android.provider.Settings
import android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
@@ -39,6 +40,7 @@ import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -48,10 +50,10 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
-import javax.inject.Inject
@SysUISingleton
-class DoNotDisturbQuickAffordanceConfig constructor(
+class DoNotDisturbQuickAffordanceConfig
+constructor(
private val context: Context,
private val controller: ZenModeController,
private val secureSettings: SecureSettings,
@@ -59,7 +61,7 @@ class DoNotDisturbQuickAffordanceConfig constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val testConditionId: Uri?,
testDialog: EnableZenModeDialog?,
-): KeyguardQuickAffordanceConfig {
+) : KeyguardQuickAffordanceConfig {
@Inject
constructor(
@@ -76,20 +78,23 @@ class DoNotDisturbQuickAffordanceConfig constructor(
private val conditionUri: Uri
get() =
- testConditionId ?: ZenModeConfig.toTimeCondition(
- context,
- settingsValue,
- userTracker.userId,
- true, /* shortVersion */
- ).id
+ testConditionId
+ ?: ZenModeConfig.toTimeCondition(
+ context,
+ settingsValue,
+ userTracker.userId,
+ true, /* shortVersion */
+ )
+ .id
private val dialog: EnableZenModeDialog by lazy {
- testDialog ?: EnableZenModeDialog(
- context,
- R.style.Theme_SystemUI_Dialog,
- true, /* cancelIsNeutral */
- ZenModeDialogMetricsLogger(context),
- )
+ testDialog
+ ?: EnableZenModeDialog(
+ context,
+ R.style.Theme_SystemUI_Dialog,
+ true, /* cancelIsNeutral */
+ ZenModeDialogMetricsLogger(context),
+ )
}
override val key: String = BuiltInKeyguardQuickAffordanceKeys.DO_NOT_DISTURB
@@ -98,58 +103,62 @@ class DoNotDisturbQuickAffordanceConfig constructor(
override val pickerIconResourceId: Int = R.drawable.ic_do_not_disturb
- override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> = combine(
- conflatedCallbackFlow {
- val callback = object: ZenModeController.Callback {
- override fun onZenChanged(zen: Int) {
- dndMode = zen
- trySendWithFailureLogging(updateState(), TAG)
- }
-
- override fun onZenAvailableChanged(available: Boolean) {
- isAvailable = available
- trySendWithFailureLogging(updateState(), TAG)
- }
- }
-
- dndMode = controller.zen
- isAvailable = controller.isZenAvailable
- trySendWithFailureLogging(updateState(), TAG)
-
- controller.addCallback(callback)
-
- awaitClose { controller.removeCallback(callback) }
- },
- secureSettings
- .observerFlow(Settings.Secure.ZEN_DURATION)
- .onStart { emit(Unit) }
- .map { secureSettings.getInt(Settings.Secure.ZEN_DURATION, ZEN_MODE_OFF) }
- .flowOn(backgroundDispatcher)
- .distinctUntilChanged()
- .onEach { settingsValue = it }
- ) { callbackFlowValue, _ -> callbackFlowValue }
+ override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
+ combine(
+ conflatedCallbackFlow {
+ val callback =
+ object : ZenModeController.Callback {
+ override fun onZenChanged(zen: Int) {
+ dndMode = zen
+ trySendWithFailureLogging(updateState(), TAG)
+ }
+
+ override fun onZenAvailableChanged(available: Boolean) {
+ isAvailable = available
+ trySendWithFailureLogging(updateState(), TAG)
+ }
+ }
+
+ dndMode = controller.zen
+ isAvailable = controller.isZenAvailable
+ trySendWithFailureLogging(updateState(), TAG)
+
+ controller.addCallback(callback)
+
+ awaitClose { controller.removeCallback(callback) }
+ },
+ secureSettings
+ .observerFlow(Settings.Secure.ZEN_DURATION)
+ .onStart { emit(Unit) }
+ .map { secureSettings.getInt(Settings.Secure.ZEN_DURATION, ZEN_MODE_OFF) }
+ .flowOn(backgroundDispatcher)
+ .distinctUntilChanged()
+ .onEach { settingsValue = it }
+ ) { callbackFlowValue, _ -> callbackFlowValue }
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
return if (controller.isZenAvailable) {
- KeyguardQuickAffordanceConfig.PickerScreenState.Default
+ KeyguardQuickAffordanceConfig.PickerScreenState.Default(
+ configureIntent = Intent(Settings.ACTION_ZEN_MODE_SETTINGS)
+ )
} else {
KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
}
}
- override fun onTriggered(expandable: Expandable?):
- KeyguardQuickAffordanceConfig.OnTriggeredResult {
+ override fun onTriggered(
+ expandable: Expandable?
+ ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
return when {
- !isAvailable ->
- KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ !isAvailable -> KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
dndMode != ZEN_MODE_OFF -> {
controller.setZen(ZEN_MODE_OFF, null, TAG)
KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
}
settingsValue == ZEN_DURATION_PROMPT ->
KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog(
- dialog.createDialog(),
- expandable
+ dialog.createDialog(),
+ expandable
)
settingsValue == ZEN_DURATION_FOREVER -> {
controller.setZen(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG)
@@ -187,4 +196,4 @@ class DoNotDisturbQuickAffordanceConfig constructor(
companion object {
const val TAG = "DoNotDisturbQuickAffordanceConfig"
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
index 62fe80a82908..3412f35669e3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
@@ -135,7 +135,7 @@ constructor(
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState =
if (flashlightController.isAvailable) {
- KeyguardQuickAffordanceConfig.PickerScreenState.Default
+ KeyguardQuickAffordanceConfig.PickerScreenState.Default()
} else {
KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index 09e5ec0065f8..a1e9137d1764 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -90,7 +90,7 @@ constructor(
)
}
- return KeyguardQuickAffordanceConfig.PickerScreenState.Default
+ return KeyguardQuickAffordanceConfig.PickerScreenState.Default()
}
override fun onTriggered(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
index 20588e9ccdc1..e32edcb010e8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -46,7 +46,7 @@ interface KeyguardQuickAffordanceConfig {
* Returns the [PickerScreenState] representing the affordance in the settings or selector
* experience.
*/
- suspend fun getPickerScreenState(): PickerScreenState = PickerScreenState.Default
+ suspend fun getPickerScreenState(): PickerScreenState = PickerScreenState.Default()
/**
* Notifies that the affordance was clicked by the user.
@@ -63,7 +63,10 @@ interface KeyguardQuickAffordanceConfig {
sealed class PickerScreenState {
/** The picker shows the item for selecting this affordance as it normally would. */
- object Default : PickerScreenState()
+ data class Default(
+ /** Optional [Intent] to use to start an activity to configure this affordance. */
+ val configureIntent: Intent? = null,
+ ) : PickerScreenState()
/**
* The picker does not show an item for selecting this affordance as it is not supported on
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
index 4f7990ff0deb..ea6c107cd161 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
@@ -89,7 +89,7 @@ constructor(
),
),
)
- else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default
+ else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index 1928f40fa059..680c06bf2c64 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -128,7 +128,7 @@ constructor(
actionComponentName = componentName,
)
}
- else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default
+ else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
index 2e34e9a93dff..41574d18e2c7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
@@ -20,6 +20,7 @@ import android.os.Build
import com.android.keyguard.ViewMediatorCallback
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
import com.android.systemui.log.dagger.BouncerLog
@@ -73,7 +74,7 @@ constructor(
* 1f = panel fully showing = bouncer fully hidden
* ```
*/
- private val _panelExpansionAmount = MutableStateFlow(KeyguardBouncer.EXPANSION_HIDDEN)
+ private val _panelExpansionAmount = MutableStateFlow(EXPANSION_HIDDEN)
val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
private val _keyguardPosition = MutableStateFlow(0f)
val keyguardPosition = _keyguardPosition.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
index e3f5e90b2300..2b2b9d0703fa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
@@ -187,6 +187,8 @@ constructor(
pickerState is KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
}
.map { (config, pickerState) ->
+ val defaultPickerState =
+ pickerState as? KeyguardQuickAffordanceConfig.PickerScreenState.Default
val disabledPickerState =
pickerState as? KeyguardQuickAffordanceConfig.PickerScreenState.Disabled
KeyguardQuickAffordancePickerRepresentation(
@@ -198,6 +200,7 @@ constructor(
instructions = disabledPickerState?.instructions,
actionText = disabledPickerState?.actionText,
actionComponentName = disabledPickerState?.actionComponentName,
+ configureIntent = defaultPickerState?.configureIntent,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index d14b66a68f11..0c4bca616e12 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -209,7 +209,7 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
return
}
- if (state == TransitionState.FINISHED) {
+ if (state == TransitionState.FINISHED || state == TransitionState.CANCELED) {
updateTransitionId = null
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 3b09ae7ba8ea..7134ec0d64f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -21,7 +21,7 @@ import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -56,7 +56,7 @@ constructor(
scope.launch {
// Using isDreamingWithOverlay provides an optimized path to LOCKSCREEN state, which
// otherwise would have gone through OCCLUDED first
- keyguardInteractor.isDreamingWithOverlay
+ keyguardInteractor.isAbleToDream
.sample(
combine(
keyguardInteractor.dozeTransitionModel,
@@ -65,8 +65,7 @@ constructor(
),
::toTriple
)
- .collect { triple ->
- val (isDreaming, dozeTransitionModel, lastStartedTransition) = triple
+ .collect { (isDreaming, dozeTransitionModel, lastStartedTransition) ->
if (
!isDreaming &&
isDozeOff(dozeTransitionModel.to) &&
@@ -96,8 +95,7 @@ constructor(
),
::toTriple
)
- .collect { triple ->
- val (isDreaming, isOccluded, lastStartedTransition) = triple
+ .collect { (isDreaming, isOccluded, lastStartedTransition) ->
if (
isOccluded &&
!isDreaming &&
@@ -123,24 +121,18 @@ constructor(
private fun listenForDreamingToGone() {
scope.launch {
- keyguardInteractor.biometricUnlockState
- .sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair)
- .collect { pair ->
- val (biometricUnlockState, keyguardState) = pair
- if (
- keyguardState == KeyguardState.DREAMING &&
- isWakeAndUnlock(biometricUnlockState)
- ) {
- keyguardTransitionRepository.startTransition(
- TransitionInfo(
- name,
- KeyguardState.DREAMING,
- KeyguardState.GONE,
- getAnimator(),
- )
+ keyguardInteractor.biometricUnlockState.collect { biometricUnlockState ->
+ if (biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.DREAMING,
+ KeyguardState.GONE,
+ getAnimator(),
)
- }
+ )
}
+ }
}
}
@@ -151,8 +143,7 @@ constructor(
keyguardTransitionInteractor.finishedKeyguardState,
::Pair
)
- .collect { pair ->
- val (dozeTransitionModel, keyguardState) = pair
+ .collect { (dozeTransitionModel, keyguardState) ->
if (
dozeTransitionModel.to == DozeStateModel.DOZE &&
keyguardState == KeyguardState.DREAMING
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 64028ceb2fbe..5674e2a15271 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -48,8 +48,6 @@ constructor(
private val keyguardTransitionRepository: KeyguardTransitionRepository,
) : TransitionInteractor(FromLockscreenTransitionInteractor::class.simpleName!!) {
- private var transitionId: UUID? = null
-
override fun start() {
listenForLockscreenToGone()
listenForLockscreenToOccluded()
@@ -104,6 +102,7 @@ constructor(
/* Starts transitions when manually dragging up the bouncer from the lockscreen. */
private fun listenForLockscreenToBouncerDragging() {
+ var transitionId: UUID? = null
scope.launch {
shadeRepository.shadeModel
.sample(
@@ -114,25 +113,43 @@ constructor(
),
::toTriple
)
- .collect { triple ->
- val (shadeModel, keyguardState, statusBarState) = triple
-
+ .collect { (shadeModel, keyguardState, statusBarState) ->
val id = transitionId
if (id != null) {
// An existing `id` means a transition is started, and calls to
- // `updateTransition` will control it until FINISHED
- keyguardTransitionRepository.updateTransition(
- id,
- 1f - shadeModel.expansionAmount,
- if (
- shadeModel.expansionAmount == 0f || shadeModel.expansionAmount == 1f
- ) {
- transitionId = null
+ // `updateTransition` will control it until FINISHED or CANCELED
+ var nextState =
+ if (shadeModel.expansionAmount == 0f) {
TransitionState.FINISHED
+ } else if (shadeModel.expansionAmount == 1f) {
+ TransitionState.CANCELED
} else {
TransitionState.RUNNING
}
+ keyguardTransitionRepository.updateTransition(
+ id,
+ 1f - shadeModel.expansionAmount,
+ nextState,
)
+
+ if (
+ nextState == TransitionState.CANCELED ||
+ nextState == TransitionState.FINISHED
+ ) {
+ transitionId = null
+ }
+
+ // If canceled, just put the state back
+ if (nextState == TransitionState.CANCELED) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = name,
+ from = KeyguardState.BOUNCER,
+ to = KeyguardState.LOCKSCREEN,
+ animator = getAnimator(0.milliseconds)
+ )
+ )
+ }
} else {
// TODO (b/251849525): Remove statusbarstate check when that state is
// integrated into KeyguardTransitionRepository
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 490d22eb0820..4cf56fe2c031 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -32,12 +32,15 @@ import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.CommandQueue.Callbacks
-import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.merge
/**
@@ -89,15 +92,23 @@ constructor(
/**
* Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means
* that doze mode is not running and DREAMING is ok to commence.
+ *
+ * Allow a brief moment to prevent rapidly oscillating between true/false signals.
*/
val isAbleToDream: Flow<Boolean> =
merge(isDreaming, isDreamingWithOverlay)
- .sample(
+ .combine(
dozeTransitionModel,
{ isDreaming, dozeTransitionModel ->
isDreaming && isDozeOff(dozeTransitionModel.to)
}
)
+ .flatMapLatest { isAbleToDream ->
+ flow {
+ delay(50)
+ emit(isAbleToDream)
+ }
+ }
.distinctUntilChanged()
/** Whether the keyguard is showing or not. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 9cdbcda1343d..ad6dbea7ae43 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -22,13 +22,17 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor
import com.android.systemui.keyguard.shared.model.AnimationParams
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import javax.inject.Inject
+import kotlin.math.max
+import kotlin.math.min
import kotlin.time.Duration
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
@@ -53,9 +57,16 @@ constructor(
val dreamingToLockscreenTransition: Flow<TransitionStep> =
repository.transition(DREAMING, LOCKSCREEN)
+ /** GONE->DREAMING transition information. */
+ val goneToDreamingTransition: Flow<TransitionStep> = repository.transition(GONE, DREAMING)
+
/** LOCKSCREEN->AOD transition information. */
val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
+ /** LOCKSCREEN->BOUNCER transition information. */
+ val lockscreenToBouncerTransition: Flow<TransitionStep> =
+ repository.transition(LOCKSCREEN, BOUNCER)
+
/** LOCKSCREEN->DREAMING transition information. */
val lockscreenToDreamingTransition: Flow<TransitionStep> =
repository.transition(LOCKSCREEN, DREAMING)
@@ -106,13 +117,23 @@ constructor(
): Flow<Float> {
val start = (params.startTime / totalDuration).toFloat()
val chunks = (totalDuration / params.duration).toFloat()
+ var isRunning = false
return flow
- // When starting, emit a value of 0f to give animations a chance to set initial state
.map { step ->
+ val value = (step.value - start) * chunks
if (step.transitionState == STARTED) {
- 0f
+ // When starting, make sure to always emit. If a transition is started from the
+ // middle, it is possible this animation is being skipped but we need to inform
+ // the ViewModels of the last update
+ isRunning = true
+ max(0f, min(1f, value))
+ } else if (isRunning && value >= 1f) {
+ // Always send a final value of 1. Because of rounding, [value] may never be
+ // exactly 1.
+ isRunning = false
+ 1f
} else {
- (step.value - start) * chunks
+ value
}
}
.filter { value -> value >= 0f && value <= 1f }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractor.kt
index c5e49c61e581..3099a497bf45 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractor.kt
@@ -18,27 +18,29 @@ package com.android.systemui.keyguard.domain.interactor
import android.view.View
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.phone.KeyguardBouncer
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
import com.android.systemui.util.ListenerSet
import javax.inject.Inject
/** Interactor to add and remove callbacks for the bouncer. */
@SysUISingleton
class PrimaryBouncerCallbackInteractor @Inject constructor() {
- private var resetCallbacks = ListenerSet<KeyguardBouncer.KeyguardResetCallback>()
- private var expansionCallbacks = ArrayList<KeyguardBouncer.PrimaryBouncerExpansionCallback>()
+ private var resetCallbacks = ListenerSet<KeyguardResetCallback>()
+ private var expansionCallbacks = ArrayList<PrimaryBouncerExpansionCallback>()
+
/** Add a KeyguardResetCallback. */
- fun addKeyguardResetCallback(callback: KeyguardBouncer.KeyguardResetCallback) {
+ fun addKeyguardResetCallback(callback: KeyguardResetCallback) {
resetCallbacks.addIfAbsent(callback)
}
/** Remove a KeyguardResetCallback. */
- fun removeKeyguardResetCallback(callback: KeyguardBouncer.KeyguardResetCallback) {
+ fun removeKeyguardResetCallback(callback: KeyguardResetCallback) {
resetCallbacks.remove(callback)
}
/** Adds a callback to listen to bouncer expansion updates. */
- fun addBouncerExpansionCallback(callback: KeyguardBouncer.PrimaryBouncerExpansionCallback) {
+ fun addBouncerExpansionCallback(callback: PrimaryBouncerExpansionCallback) {
if (!expansionCallbacks.contains(callback)) {
expansionCallbacks.add(callback)
}
@@ -48,7 +50,7 @@ class PrimaryBouncerCallbackInteractor @Inject constructor() {
* Removes a previously added callback. If the callback was never added, this method does
* nothing.
*/
- fun removeBouncerExpansionCallback(callback: KeyguardBouncer.PrimaryBouncerExpansionCallback) {
+ fun removeBouncerExpansionCallback(callback: PrimaryBouncerExpansionCallback) {
expansionCallbacks.remove(callback)
}
@@ -99,4 +101,40 @@ class PrimaryBouncerCallbackInteractor @Inject constructor() {
callback.onKeyguardReset()
}
}
+
+ /** Callback updated when the primary bouncer's show and hide states change. */
+ interface PrimaryBouncerExpansionCallback {
+ /**
+ * Invoked when the bouncer expansion reaches [EXPANSION_VISIBLE]. This is NOT called each
+ * time the bouncer is shown, but rather only when the fully shown amount has changed based
+ * on the panel expansion. The bouncer's visibility can still change when the expansion
+ * amount hasn't changed. See [PrimaryBouncerInteractor.isFullyShowing] for the checks for
+ * the bouncer showing state.
+ */
+ fun onFullyShown() {}
+
+ /** Invoked when the bouncer is starting to transition to a hidden state. */
+ fun onStartingToHide() {}
+
+ /** Invoked when the bouncer is starting to transition to a visible state. */
+ fun onStartingToShow() {}
+
+ /** Invoked when the bouncer expansion reaches [EXPANSION_HIDDEN]. */
+ fun onFullyHidden() {}
+
+ /**
+ * From 0f [EXPANSION_VISIBLE] when fully visible to 1f [EXPANSION_HIDDEN] when fully hidden
+ */
+ fun onExpansionChanged(bouncerHideAmount: Float) {}
+
+ /**
+ * Invoked when visibility of KeyguardBouncer has changed. Note the bouncer expansion can be
+ * [EXPANSION_VISIBLE], but the view's visibility can be [View.INVISIBLE].
+ */
+ fun onVisibilityChanged(isVisible: Boolean) {}
+ }
+
+ interface KeyguardResetCallback {
+ fun onKeyguardReset()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index 2cf5fb98d07e..a92540d733b5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -32,11 +32,11 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.shared.system.SysUiStatsLog
-import com.android.systemui.statusbar.phone.KeyguardBouncer
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
@@ -143,7 +143,7 @@ constructor(
Trace.beginSection("KeyguardBouncer#show")
repository.setPrimaryScrimmed(isScrimmed)
if (isScrimmed) {
- setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE)
+ setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
}
if (resumeBouncer) {
@@ -204,14 +204,14 @@ constructor(
}
if (
- expansion == KeyguardBouncer.EXPANSION_VISIBLE &&
- oldExpansion != KeyguardBouncer.EXPANSION_VISIBLE
+ expansion == KeyguardBouncerConstants.EXPANSION_VISIBLE &&
+ oldExpansion != KeyguardBouncerConstants.EXPANSION_VISIBLE
) {
falsingCollector.onBouncerShown()
primaryBouncerCallbackInteractor.dispatchFullyShown()
} else if (
- expansion == KeyguardBouncer.EXPANSION_HIDDEN &&
- oldExpansion != KeyguardBouncer.EXPANSION_HIDDEN
+ expansion == KeyguardBouncerConstants.EXPANSION_HIDDEN &&
+ oldExpansion != KeyguardBouncerConstants.EXPANSION_HIDDEN
) {
/*
* There are cases where #hide() was not invoked, such as when
@@ -222,8 +222,8 @@ constructor(
DejankUtils.postAfterTraversal { primaryBouncerCallbackInteractor.dispatchReset() }
primaryBouncerCallbackInteractor.dispatchFullyHidden()
} else if (
- expansion != KeyguardBouncer.EXPANSION_VISIBLE &&
- oldExpansion == KeyguardBouncer.EXPANSION_VISIBLE
+ expansion != KeyguardBouncerConstants.EXPANSION_VISIBLE &&
+ oldExpansion == KeyguardBouncerConstants.EXPANSION_VISIBLE
) {
primaryBouncerCallbackInteractor.dispatchStartingToHide()
repository.setPrimaryStartingToHide(true)
@@ -303,7 +303,7 @@ constructor(
fun isFullyShowing(): Boolean {
return (repository.primaryBouncerShowingSoon.value ||
repository.primaryBouncerVisible.value) &&
- repository.panelExpansionAmount.value == KeyguardBouncer.EXPANSION_VISIBLE &&
+ repository.panelExpansionAmount.value == KeyguardBouncerConstants.EXPANSION_VISIBLE &&
repository.primaryBouncerStartingDisappearAnimation.value == null
}
@@ -315,8 +315,8 @@ constructor(
/** If bouncer expansion is between 0f and 1f non-inclusive. */
fun isInTransit(): Boolean {
return repository.primaryBouncerShowingSoon.value ||
- repository.panelExpansionAmount.value != KeyguardBouncer.EXPANSION_HIDDEN &&
- repository.panelExpansionAmount.value != KeyguardBouncer.EXPANSION_VISIBLE
+ repository.panelExpansionAmount.value != KeyguardBouncerConstants.EXPANSION_HIDDEN &&
+ repository.panelExpansionAmount.value != KeyguardBouncerConstants.EXPANSION_VISIBLE
}
/** Return whether bouncer is animating away. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt
new file mode 100644
index 000000000000..bb5ac84c6e54
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.shared.constants
+
+object KeyguardBouncerConstants {
+ /**
+ * Values for the bouncer expansion represented as the panel expansion. Panel expansion 1f =
+ * panel fully showing = bouncer fully hidden Panel expansion 0f = panel fully hiding = bouncer
+ * fully showing
+ */
+ const val EXPANSION_HIDDEN = 1f
+ const val EXPANSION_VISIBLE = 0f
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt
index 7d133598e105..e7e915940290 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.shared.model
+import android.content.Intent
import androidx.annotation.DrawableRes
/**
@@ -45,4 +46,7 @@ data class KeyguardQuickAffordancePickerRepresentation(
* user to a destination where they can re-enable it.
*/
val actionComponentName: String? = null,
+
+ /** Optional [Intent] to use to start an activity to configure this affordance. */
+ val configureIntent: Intent? = null,
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 9d8bf7deb03e..d020529d2bae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -342,14 +342,12 @@ object KeyguardBottomAreaViewBinder {
private val longPressDurationMs = ViewConfiguration.getLongPressTimeout().toLong()
private var longPressAnimator: ViewPropertyAnimator? = null
- private var downTimestamp = 0L
@SuppressLint("ClickableViewAccessibility")
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
return when (event?.actionMasked) {
MotionEvent.ACTION_DOWN ->
if (viewModel.configKey != null) {
- downTimestamp = System.currentTimeMillis()
longPressAnimator =
view
.animate()
@@ -396,7 +394,7 @@ object KeyguardBottomAreaViewBinder {
MotionEvent.ACTION_UP -> {
cancel(
onAnimationEnd =
- if (System.currentTimeMillis() - downTimestamp < longPressDurationMs) {
+ if (event.eventTime - event.downTime < longPressDurationMs) {
Runnable {
messageDisplayer.invoke(
R.string.keyguard_affordance_press_too_short
@@ -437,7 +435,6 @@ object KeyguardBottomAreaViewBinder {
}
private fun cancel(onAnimationEnd: Runnable? = null) {
- downTimestamp = 0L
longPressAnimator?.cancel()
longPressAnimator = null
view.animate().scaleX(1f).scaleY(1f).withEndAction(onAnimationEnd)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index f772b17a7fb6..5e46c5d1f67a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.binder
import android.view.KeyEvent
import android.view.View
import android.view.ViewGroup
+import android.window.OnBackAnimationCallback
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.internal.policy.SystemBarUtils
@@ -27,10 +28,10 @@ import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.keyguard.data.BouncerViewDelegate
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@@ -55,6 +56,10 @@ object KeyguardBouncerViewBinder {
mode == KeyguardSecurityModel.SecurityMode.SimPuk
}
+ override fun getBackCallback(): OnBackAnimationCallback {
+ return hostViewController.backCallback
+ }
+
override fun shouldDismissOnMenuPressed(): Boolean {
return hostViewController.shouldEnableMenuKey()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index e164f5d58b07..6627865ecc79 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -22,10 +22,14 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.AnimationParams
+import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
/**
* Breaks down DREAMING->LOCKSCREEN transition into discrete steps for corresponding views to
@@ -49,9 +53,15 @@ constructor(
/** Lockscreen views y-translation */
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
- return flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
- -translatePx + (EMPHASIZED_DECELERATE.getInterpolation(value) * translatePx)
- }
+ return merge(
+ flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
+ -translatePx + (EMPHASIZED_DECELERATE.getInterpolation(value) * translatePx)
+ },
+ // On end, reset the translation to 0
+ interactor.dreamingToLockscreenTransition
+ .filter { it.transitionState == FINISHED || it.transitionState == CANCELED }
+ .map { 0f }
+ )
}
/** Lockscreen views alpha */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
new file mode 100644
index 000000000000..5a4796096eeb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.AnimationParams
+import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+/** Breaks down GONE->DREAMING transition into discrete steps for corresponding views to consume. */
+@SysUISingleton
+class GoneToDreamingTransitionViewModel
+@Inject
+constructor(
+ private val interactor: KeyguardTransitionInteractor,
+) {
+
+ /** Lockscreen views y-translation */
+ fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
+ return merge(
+ flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
+ (EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx)
+ },
+ // On end, reset the translation to 0
+ interactor.goneToDreamingTransition
+ .filter { it.transitionState == FINISHED || it.transitionState == CANCELED }
+ .map { 0f }
+ )
+ }
+
+ /** Lockscreen views alpha */
+ val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA).map { 1f - it }
+
+ private fun flowForAnimation(params: AnimationParams): Flow<Float> {
+ return interactor.transitionStepAnimation(
+ interactor.goneToDreamingTransition,
+ params,
+ totalDuration = TO_DREAMING_DURATION
+ )
+ }
+
+ companion object {
+ val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = 500.milliseconds)
+ val LOCKSCREEN_ALPHA = AnimationParams(duration = 250.milliseconds)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
index e5d4e4971baa..c6002d6db91a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -20,9 +20,9 @@ import android.view.View
import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.BouncerViewDelegate
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
-import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
index d48f87deaaf4..e05adbdab583 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
@@ -21,7 +21,8 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.AnimationParams
-import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -48,7 +49,7 @@ constructor(
},
// On end, reset the translation to 0
interactor.lockscreenToDreamingTransition
- .filter { step -> step.transitionState == TransitionState.FINISHED }
+ .filter { it.transitionState == FINISHED || it.transitionState == CANCELED }
.map { 0f }
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
index e3649187b0a7..d69ac7fe035d 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
@@ -145,7 +145,7 @@ private fun createLifecycleOwnerAndRun(
* └───────────────┴───────────────────┴──────────────┴─────────────────┘
* ```
*/
-private class ViewLifecycleOwner(
+class ViewLifecycleOwner(
private val view: View,
) : LifecycleOwner {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index ceb48458a1d3..a692ad74615f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -18,6 +18,7 @@ package com.android.systemui.media
import android.app.ActivityOptions
import android.content.Intent
import android.content.res.Configuration
+import android.content.res.Resources
import android.media.projection.IMediaProjection
import android.media.projection.MediaProjectionManager.EXTRA_MEDIA_PROJECTION
import android.os.Binder
@@ -27,6 +28,7 @@ import android.os.ResultReceiver
import android.os.UserHandle
import android.view.ViewGroup
import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider
import com.android.internal.app.ChooserActivity
import com.android.internal.app.ResolverListController
import com.android.internal.app.chooser.NotSelectableTargetInfo
@@ -59,16 +61,12 @@ class MediaProjectionAppSelectorActivity(
private lateinit var configurationController: ConfigurationController
private lateinit var controller: MediaProjectionAppSelectorController
private lateinit var recentsViewController: MediaProjectionRecentsViewController
+ private lateinit var component: MediaProjectionAppSelectorComponent
override fun getLayoutResource() = R.layout.media_projection_app_selector
public override fun onCreate(bundle: Bundle?) {
- val component =
- componentFactory.create(
- activity = this,
- view = this,
- resultHandler = this
- )
+ component = componentFactory.create(activity = this, view = this, resultHandler = this)
// Create a separate configuration controller for this activity as the configuration
// might be different from the global one
@@ -76,11 +74,12 @@ class MediaProjectionAppSelectorActivity(
controller = component.controller
recentsViewController = component.recentsViewController
- val queryIntent = Intent(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) }
- intent.putExtra(Intent.EXTRA_INTENT, queryIntent)
+ intent.configureChooserIntent(
+ resources,
+ component.hostUserHandle,
+ component.personalProfileUserHandle
+ )
- val title = getString(R.string.media_projection_permission_app_selector_title)
- intent.putExtra(Intent.EXTRA_TITLE, title)
super.onCreate(bundle)
controller.init()
}
@@ -183,6 +182,13 @@ class MediaProjectionAppSelectorActivity(
override fun shouldShowContentPreview() = true
+ override fun shouldShowContentPreviewWhenEmpty(): Boolean = true
+
+ override fun createMyUserIdProvider(): MyUserIdProvider =
+ object : MyUserIdProvider() {
+ override fun getMyUserId(): Int = component.hostUserHandle.identifier
+ }
+
override fun createContentPreviewView(parent: ViewGroup): ViewGroup =
recentsViewController.createView(parent)
@@ -193,6 +199,34 @@ class MediaProjectionAppSelectorActivity(
* instance through activity result.
*/
const val EXTRA_CAPTURE_REGION_RESULT_RECEIVER = "capture_region_result_receiver"
+
+ /** UID of the app that originally launched the media projection flow (host app user) */
+ const val EXTRA_HOST_APP_USER_HANDLE = "launched_from_user_handle"
const val KEY_CAPTURE_TARGET = "capture_region"
+
+ /** Set up intent for the [ChooserActivity] */
+ private fun Intent.configureChooserIntent(
+ resources: Resources,
+ hostUserHandle: UserHandle,
+ personalProfileUserHandle: UserHandle
+ ) {
+ // Specify the query intent to show icons for all apps on the chooser screen
+ val queryIntent =
+ Intent(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) }
+ putExtra(Intent.EXTRA_INTENT, queryIntent)
+
+ // Update the title of the chooser
+ val title = resources.getString(R.string.media_projection_permission_app_selector_title)
+ putExtra(Intent.EXTRA_TITLE, title)
+
+ // Select host app's profile tab by default
+ val selectedProfile =
+ if (hostUserHandle == personalProfileUserHandle) {
+ PROFILE_PERSONAL
+ } else {
+ PROFILE_WORK
+ }
+ putExtra(EXTRA_SELECTED_PROFILE, selectedProfile)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index bfa67a89baca..d830fc4a5fb5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -22,6 +22,7 @@ import static com.android.systemui.screenrecord.ScreenShareOptionKt.ENTIRE_SCREE
import static com.android.systemui.screenrecord.ScreenShareOptionKt.SINGLE_APP;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
@@ -35,6 +36,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.text.BidiFormatter;
import android.text.SpannableString;
import android.text.TextPaint;
@@ -208,8 +210,14 @@ public class MediaProjectionPermissionActivity extends Activity
final Intent intent = new Intent(this, MediaProjectionAppSelectorActivity.class);
intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
projection.asBinder());
+ intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
+ UserHandle.getUserHandleForUid(getLaunchedFromUid()));
intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
- startActivity(intent);
+
+ // Start activity from the current foreground user to avoid creating a separate
+ // SystemUI process without access to recent tasks because it won't have
+ // WM Shell running inside.
+ startActivityAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser()));
}
} catch (RemoteException e) {
Log.e(TAG, "Error granting projection permission", e);
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
index f006442906e7..be18cbec7163 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
@@ -88,7 +88,10 @@ data class MediaData(
val instanceId: InstanceId,
/** The UID of the app, used for logging */
- val appUid: Int
+ val appUid: Int,
+
+ /** Whether explicit indicator exists */
+ val isExplicit: Boolean = false,
) {
companion object {
/** Media is playing on the local device */
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
index a8f39fa9a456..1c8bfd1fc468 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
@@ -24,6 +24,7 @@ import android.widget.ImageView
import android.widget.SeekBar
import android.widget.TextView
import androidx.constraintlayout.widget.Barrier
+import com.android.internal.widget.CachingIconView
import com.android.systemui.R
import com.android.systemui.media.controls.models.GutsViewHolder
import com.android.systemui.surfaceeffects.ripple.MultiRippleView
@@ -44,6 +45,7 @@ class MediaViewHolder constructor(itemView: View) {
val appIcon = itemView.requireViewById<ImageView>(R.id.icon)
val titleText = itemView.requireViewById<TextView>(R.id.header_title)
val artistText = itemView.requireViewById<TextView>(R.id.header_artist)
+ val explicitIndicator = itemView.requireViewById<CachingIconView>(R.id.media_explicit_indicator)
// Output switcher
val seamless = itemView.requireViewById<ViewGroup>(R.id.media_seamless)
@@ -123,6 +125,7 @@ class MediaViewHolder constructor(itemView: View) {
R.id.app_name,
R.id.header_title,
R.id.header_artist,
+ R.id.media_explicit_indicator,
R.id.media_seamless,
R.id.media_progress_bar,
R.id.actionPlayPause,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 2dd339d409a6..a13279717d05 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -45,6 +45,7 @@ import android.os.Process
import android.os.UserHandle
import android.provider.Settings
import android.service.notification.StatusBarNotification
+import android.support.v4.media.MediaMetadataCompat
import android.text.TextUtils
import android.util.Log
import androidx.media.utils.MediaConstants
@@ -660,6 +661,10 @@ class MediaDataManager(
val currentEntry = mediaEntries.get(packageName)
val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
val appUid = currentEntry?.appUid ?: Process.INVALID_UID
+ val isExplicit =
+ desc.extras?.getLong(MediaConstants.METADATA_KEY_IS_EXPLICIT) ==
+ MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT &&
+ mediaFlags.isExplicitIndicatorEnabled()
val mediaAction = getResumeMediaAction(resumeAction)
val lastActive = systemClock.elapsedRealtime()
@@ -689,7 +694,8 @@ class MediaDataManager(
hasCheckedForResume = true,
lastActive = lastActive,
instanceId = instanceId,
- appUid = appUid
+ appUid = appUid,
+ isExplicit = isExplicit,
)
)
}
@@ -750,6 +756,15 @@ class MediaDataManager(
song = HybridGroupManager.resolveTitle(notif)
}
+ // Explicit Indicator
+ var isExplicit = false
+ if (mediaFlags.isExplicitIndicatorEnabled()) {
+ val mediaMetadataCompat = MediaMetadataCompat.fromMediaMetadata(metadata)
+ isExplicit =
+ mediaMetadataCompat?.getLong(MediaConstants.METADATA_KEY_IS_EXPLICIT) ==
+ MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT
+ }
+
// Artist name
var artist: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_ARTIST)
if (artist == null) {
@@ -848,10 +863,11 @@ class MediaDataManager(
notificationKey = key,
hasCheckedForResume = hasCheckedForResume,
isPlaying = isPlaying,
- isClearable = sbn.isClearable(),
+ isClearable = !sbn.isOngoing,
lastActive = lastActive,
instanceId = instanceId,
- appUid = appUid
+ appUid = appUid,
+ isExplicit = isExplicit,
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
index 899148b0014c..8f1c9048026f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
@@ -130,7 +130,12 @@ constructor(
private var splitShadeContainer: ViewGroup? = null
/** Track the media player setting status on lock screen. */
- private var allowMediaPlayerOnLockScreen: Boolean = true
+ private var allowMediaPlayerOnLockScreen: Boolean =
+ secureSettings.getBoolForUser(
+ Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
+ true,
+ UserHandle.USER_CURRENT
+ )
private val lockScreenMediaPlayerUri =
secureSettings.getUriFor(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 15c34430f455..45d50f0e4976 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -50,7 +50,6 @@ import android.os.Process;
import android.os.Trace;
import android.text.TextUtils;
import android.util.Log;
-import android.util.Pair;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -68,6 +67,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
+import com.android.internal.widget.CachingIconView;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.R;
@@ -111,8 +111,11 @@ import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimat
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController;
import com.android.systemui.util.ColorUtilKt;
import com.android.systemui.util.animation.TransitionLayout;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
+import dagger.Lazy;
+
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
@@ -120,7 +123,7 @@ import java.util.concurrent.Executor;
import javax.inject.Inject;
-import dagger.Lazy;
+import kotlin.Triple;
import kotlin.Unit;
/**
@@ -166,10 +169,13 @@ public class MediaControlPanel {
R.id.action1
);
+ // Time in millis for playing turbulence noise that is played after a touch ripple.
+ @VisibleForTesting static final long TURBULENCE_NOISE_PLAY_DURATION = 7500L;
+
private final SeekBarViewModel mSeekBarViewModel;
private SeekBarObserver mSeekBarObserver;
protected final Executor mBackgroundExecutor;
- private final Executor mMainExecutor;
+ private final DelayableExecutor mMainExecutor;
private final ActivityStarter mActivityStarter;
private final BroadcastSender mBroadcastSender;
@@ -222,10 +228,10 @@ public class MediaControlPanel {
private String mSwitchBroadcastApp;
private MultiRippleController mMultiRippleController;
private TurbulenceNoiseController mTurbulenceNoiseController;
- private FeatureFlags mFeatureFlags;
- private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig = null;
+ private final FeatureFlags mFeatureFlags;
+ private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig;
@VisibleForTesting
- MultiRippleController.Companion.RipplesFinishedListener mRipplesFinishedListener = null;
+ MultiRippleController.Companion.RipplesFinishedListener mRipplesFinishedListener;
/**
* Initialize a new control panel
@@ -239,7 +245,7 @@ public class MediaControlPanel {
public MediaControlPanel(
Context context,
@Background Executor backgroundExecutor,
- @Main Executor mainExecutor,
+ @Main DelayableExecutor mainExecutor,
ActivityStarter activityStarter,
BroadcastSender broadcastSender,
MediaViewController mediaViewController,
@@ -398,10 +404,11 @@ public class MediaControlPanel {
TextView titleText = mMediaViewHolder.getTitleText();
TextView artistText = mMediaViewHolder.getArtistText();
+ CachingIconView explicitIndicator = mMediaViewHolder.getExplicitIndicator();
AnimatorSet enter = loadAnimator(R.anim.media_metadata_enter,
- Interpolators.EMPHASIZED_DECELERATE, titleText, artistText);
+ Interpolators.EMPHASIZED_DECELERATE, titleText, artistText, explicitIndicator);
AnimatorSet exit = loadAnimator(R.anim.media_metadata_exit,
- Interpolators.EMPHASIZED_ACCELERATE, titleText, artistText);
+ Interpolators.EMPHASIZED_ACCELERATE, titleText, artistText, explicitIndicator);
MultiRippleView multiRippleView = vh.getMultiRippleView();
mMultiRippleController = new MultiRippleController(multiRippleView);
@@ -409,10 +416,12 @@ public class MediaControlPanel {
if (mFeatureFlags.isEnabled(Flags.UMO_TURBULENCE_NOISE)) {
mRipplesFinishedListener = () -> {
if (mTurbulenceNoiseAnimationConfig == null) {
- mTurbulenceNoiseAnimationConfig = createLingeringNoiseAnimation();
+ mTurbulenceNoiseAnimationConfig = createTurbulenceNoiseAnimation();
}
// Color will be correctly updated in ColorSchemeTransition.
mTurbulenceNoiseController.play(mTurbulenceNoiseAnimationConfig);
+ mMainExecutor.executeDelayed(
+ mTurbulenceNoiseController::finish, TURBULENCE_NOISE_PLAY_DURATION);
};
mMultiRippleController.addRipplesFinishedListener(mRipplesFinishedListener);
}
@@ -664,11 +673,15 @@ public class MediaControlPanel {
private boolean bindSongMetadata(MediaData data) {
TextView titleText = mMediaViewHolder.getTitleText();
TextView artistText = mMediaViewHolder.getArtistText();
+ ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
+ ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
return mMetadataAnimationHandler.setNext(
- Pair.create(data.getSong(), data.getArtist()),
+ new Triple(data.getSong(), data.getArtist(), data.isExplicit()),
() -> {
titleText.setText(data.getSong());
artistText.setText(data.getArtist());
+ setVisibleAndAlpha(expandedSet, R.id.media_explicit_indicator, data.isExplicit());
+ setVisibleAndAlpha(collapsedSet, R.id.media_explicit_indicator, data.isExplicit());
// refreshState is required here to resize the text views (and prevent ellipsis)
mMediaViewController.refreshState();
@@ -1056,7 +1069,7 @@ public class MediaControlPanel {
);
}
- private TurbulenceNoiseAnimationConfig createLingeringNoiseAnimation() {
+ private TurbulenceNoiseAnimationConfig createTurbulenceNoiseAnimation() {
return new TurbulenceNoiseAnimationConfig(
TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_GRID_COUNT,
TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER,
@@ -1071,7 +1084,9 @@ public class MediaControlPanel {
/* width= */ mMediaViewHolder.getMultiRippleView().getWidth(),
/* height= */ mMediaViewHolder.getMultiRippleView().getHeight(),
TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS,
+ /* easeInDuration= */
TurbulenceNoiseAnimationConfig.DEFAULT_EASING_DURATION_IN_MILLIS,
+ /* easeOutDuration= */
TurbulenceNoiseAnimationConfig.DEFAULT_EASING_DURATION_IN_MILLIS,
this.getContext().getResources().getDisplayMetrics().density,
BlendMode.PLUS,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
index 71cc47a53b52..2ec7be6eaa32 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
@@ -77,6 +77,7 @@ constructor(
setOf(
R.id.header_title,
R.id.header_artist,
+ R.id.media_explicit_indicator,
R.id.actionPlayPause,
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index 8d4931a5d08c..5bc35caed515 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -42,4 +42,7 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) {
* [android.app.StatusBarManager.registerNearbyMediaDevicesProvider] for more information.
*/
fun areNearbyMediaDevicesEnabled() = featureFlags.isEnabled(Flags.MEDIA_NEARBY_DEVICES)
+
+ /** Check whether we show explicit indicator on UMO */
+ fun isExplicitIndicatorEnabled() = featureFlags.isEnabled(Flags.MEDIA_EXPLICIT_INDICATOR)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java
new file mode 100644
index 000000000000..e35575bfc184
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.dialog;
+
+import android.annotation.MainThread;
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.statusbar.CommandQueue;
+
+import javax.inject.Inject;
+
+/** Controls display of media output switcher. */
+@SysUISingleton
+public class MediaOutputSwitcherDialogUI implements CoreStartable, CommandQueue.Callbacks {
+
+ private static final String TAG = "MediaOutputSwitcherDialogUI";
+
+ private final CommandQueue mCommandQueue;
+ private final MediaOutputDialogFactory mMediaOutputDialogFactory;
+ private final FeatureFlags mFeatureFlags;
+
+ @Inject
+ public MediaOutputSwitcherDialogUI(
+ Context context,
+ CommandQueue commandQueue,
+ MediaOutputDialogFactory mediaOutputDialogFactory,
+ FeatureFlags featureFlags) {
+ mCommandQueue = commandQueue;
+ mMediaOutputDialogFactory = mediaOutputDialogFactory;
+ mFeatureFlags = featureFlags;
+ }
+
+ @Override
+ public void start() {
+ if (mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_SHOW_API_ENABLED)) {
+ mCommandQueue.addCallback(this);
+ } else {
+ Log.w(TAG, "Show media output switcher is not enabled.");
+ }
+ }
+
+ @Override
+ @MainThread
+ public void showMediaOutputSwitcher(String packageName) {
+ if (!TextUtils.isEmpty(packageName)) {
+ mMediaOutputDialogFactory.create(packageName, false, null);
+ } else {
+ Log.e(TAG, "Unable to launch media output dialog. Package name is empty.");
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 6c41caab38a8..1d863435fa6e 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -19,9 +19,11 @@ package com.android.systemui.mediaprojection.appselector
import android.app.Activity
import android.content.ComponentName
import android.content.Context
+import android.os.UserHandle
import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.MediaProjectionAppSelectorActivity
+import com.android.systemui.media.MediaProjectionAppSelectorActivity.Companion.EXTRA_HOST_APP_USER_HANDLE
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
import com.android.systemui.mediaprojection.appselector.data.IconLoaderLibAppIconLoader
@@ -30,6 +32,8 @@ import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnail
import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider
import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController
import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
import com.android.systemui.statusbar.policy.ConfigurationController
import dagger.Binds
@@ -39,6 +43,7 @@ import dagger.Provides
import dagger.Subcomponent
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import java.lang.IllegalArgumentException
import javax.inject.Qualifier
import javax.inject.Scope
import kotlinx.coroutines.CoroutineScope
@@ -46,6 +51,12 @@ import kotlinx.coroutines.SupervisorJob
@Qualifier @Retention(AnnotationRetention.BINARY) annotation class MediaProjectionAppSelector
+@Qualifier @Retention(AnnotationRetention.BINARY) annotation class HostUserHandle
+
+@Qualifier @Retention(AnnotationRetention.BINARY) annotation class PersonalProfile
+
+@Qualifier @Retention(AnnotationRetention.BINARY) annotation class WorkProfile
+
@Retention(AnnotationRetention.RUNTIME) @Scope annotation class MediaProjectionAppSelectorScope
@Module(subcomponents = [MediaProjectionAppSelectorComponent::class])
@@ -83,7 +94,7 @@ interface MediaProjectionAppSelectorModule {
@MediaProjectionAppSelector
@MediaProjectionAppSelectorScope
fun provideAppSelectorComponentName(context: Context): ComponentName =
- ComponentName(context, MediaProjectionAppSelectorActivity::class.java)
+ ComponentName(context, MediaProjectionAppSelectorActivity::class.java)
@Provides
@MediaProjectionAppSelector
@@ -93,9 +104,32 @@ interface MediaProjectionAppSelectorModule {
): ConfigurationController = ConfigurationControllerImpl(activity)
@Provides
- fun bindIconFactory(
- context: Context
- ): IconFactory = IconFactory.obtain(context)
+ @PersonalProfile
+ @MediaProjectionAppSelectorScope
+ fun personalUserHandle(activityManagerWrapper: ActivityManagerWrapper): UserHandle {
+ // Current foreground user is the 'personal' profile
+ return UserHandle.of(activityManagerWrapper.currentUserId)
+ }
+
+ @Provides
+ @WorkProfile
+ @MediaProjectionAppSelectorScope
+ fun workProfileUserHandle(userTracker: UserTracker): UserHandle? =
+ userTracker.userProfiles.find { it.isManagedProfile }?.userHandle
+
+ @Provides
+ @HostUserHandle
+ @MediaProjectionAppSelectorScope
+ fun hostUserHandle(activity: MediaProjectionAppSelectorActivity): UserHandle {
+ val extras =
+ activity.intent.extras
+ ?: error("MediaProjectionAppSelectorActivity should be launched with extras")
+ return extras.getParcelable(EXTRA_HOST_APP_USER_HANDLE)
+ ?: error("MediaProjectionAppSelectorActivity should be provided with " +
+ "$EXTRA_HOST_APP_USER_HANDLE extra")
+ }
+
+ @Provides fun bindIconFactory(context: Context): IconFactory = IconFactory.obtain(context)
@Provides
@MediaProjectionAppSelector
@@ -124,6 +158,8 @@ interface MediaProjectionAppSelectorComponent {
val controller: MediaProjectionAppSelectorController
val recentsViewController: MediaProjectionRecentsViewController
+ @get:HostUserHandle val hostUserHandle: UserHandle
+ @get:PersonalProfile val personalProfileUserHandle: UserHandle
@MediaProjectionAppSelector val configurationController: ConfigurationController
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
index d744a40b60d8..52c7ca3bb3d4 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
@@ -17,24 +17,36 @@
package com.android.systemui.mediaprojection.appselector
import android.content.ComponentName
+import android.os.UserHandle
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
-import javax.inject.Inject
@MediaProjectionAppSelectorScope
-class MediaProjectionAppSelectorController @Inject constructor(
+class MediaProjectionAppSelectorController
+@Inject
+constructor(
private val recentTaskListProvider: RecentTaskListProvider,
private val view: MediaProjectionAppSelectorView,
+ private val flags: FeatureFlags,
+ @HostUserHandle private val hostUserHandle: UserHandle,
@MediaProjectionAppSelector private val scope: CoroutineScope,
@MediaProjectionAppSelector private val appSelectorComponentName: ComponentName
) {
fun init() {
scope.launch {
- val tasks = recentTaskListProvider.loadRecentTasks().sortTasks()
+ val recentTasks = recentTaskListProvider.loadRecentTasks()
+
+ val tasks = recentTasks
+ .filterDevicePolicyRestrictedTasks()
+ .sortedTasks()
+
view.bind(tasks)
}
}
@@ -43,9 +55,20 @@ class MediaProjectionAppSelectorController @Inject constructor(
scope.cancel()
}
- private fun List<RecentTask>.sortTasks(): List<RecentTask> =
- sortedBy {
- // Show normal tasks first and only then tasks with opened app selector
- it.topActivityComponent == appSelectorComponentName
+ /**
+ * Removes all recent tasks that are different from the profile of the host app to avoid any
+ * cross-profile sharing
+ */
+ private fun List<RecentTask>.filterDevicePolicyRestrictedTasks(): List<RecentTask> =
+ if (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) {
+ // TODO(b/263950746): filter tasks based on the enterprise policies
+ this
+ } else {
+ filter { UserHandle.of(it.userId) == hostUserHandle }
}
+
+ private fun List<RecentTask>.sortedTasks(): List<RecentTask> = sortedBy {
+ // Show normal tasks first and only then tasks with opened app selector
+ it.topActivityComponent == appSelectorComponentName
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
index cd994b857e95..41e22860d0ad 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
@@ -17,11 +17,12 @@
package com.android.systemui.mediaprojection.appselector.data
import android.annotation.ColorInt
+import android.annotation.UserIdInt
import android.content.ComponentName
data class RecentTask(
val taskId: Int,
- val userId: Int,
+ @UserIdInt val userId: Int,
val topActivityComponent: ComponentName?,
val baseIntentComponent: ComponentName?,
@ColorInt val colorBackground: Int?
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 5b306c98912d..61bb858ee7d7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -16,6 +16,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_SWITCHER_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
@@ -44,6 +45,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A
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_NAV_BAR_HIDDEN;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
@@ -136,9 +138,10 @@ import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.rotation.RotationButton;
import com.android.systemui.shared.rotation.RotationButtonController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
@@ -252,6 +255,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private final AutoHideController.Factory mAutoHideControllerFactory;
private final Optional<TelecomManager> mTelecomManagerOptional;
private final InputMethodManager mInputMethodManager;
+ private final TaskStackChangeListeners mTaskStackChangeListeners;
@VisibleForTesting
public int mDisplayId;
@@ -488,6 +492,18 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
};
+ private boolean mScreenPinningActive = false;
+ private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+ @Override
+ public void onLockTaskModeChanged(int mode) {
+ mScreenPinningActive = (mode == LOCK_TASK_MODE_PINNED);
+ mSysUiFlagsContainer.setFlag(SYSUI_STATE_SCREEN_PINNING, mScreenPinningActive)
+ .commitUpdate(mDisplayId);
+ mView.setInScreenPinning(mScreenPinningActive);
+ updateScreenPinningGestures();
+ }
+ };
+
@Inject
NavigationBar(
NavigationBarView navigationBarView,
@@ -529,7 +545,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
EdgeBackGestureHandler edgeBackGestureHandler,
Optional<BackAnimation> backAnimation,
UserContextProvider userContextProvider,
- WakefulnessLifecycle wakefulnessLifecycle) {
+ WakefulnessLifecycle wakefulnessLifecycle,
+ TaskStackChangeListeners taskStackChangeListeners) {
super(navigationBarView);
mFrame = navigationBarFrame;
mContext = context;
@@ -568,6 +585,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mInputMethodManager = inputMethodManager;
mUserContextProvider = userContextProvider;
mWakefulnessLifecycle = wakefulnessLifecycle;
+ mTaskStackChangeListeners = taskStackChangeListeners;
mNavColorSampleMargin = getResources()
.getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
@@ -676,6 +694,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mCommandQueue.recomputeDisableFlags(mDisplayId, false);
mNotificationShadeDepthController.addListener(mDepthListener);
+ mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
}
public void destroyView() {
@@ -689,6 +708,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mNotificationShadeDepthController.removeListener(mDepthListener);
mDeviceConfigProxy.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
+ mTaskStackChangeListeners.unregisterTaskStackListener(mTaskStackListener);
}
@Override
@@ -990,11 +1010,15 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
pw.println(" mTransientShown=" + mTransientShown);
pw.println(" mTransientShownFromGestureOnSystemBar="
+ mTransientShownFromGestureOnSystemBar);
+ pw.println(" mScreenPinningActive=" + mScreenPinningActive);
dumpBarTransitions(pw, "mNavigationBarView", getBarTransitions());
pw.println(" mOrientedHandleSamplingRegion: " + mOrientedHandleSamplingRegion);
mView.dump(pw);
mRegionSamplingHelper.dump(pw);
+ if (mAutoHideController != null) {
+ mAutoHideController.dump(pw);
+ }
}
// ----- CommandQueue Callbacks -----
@@ -1213,10 +1237,9 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private void updateScreenPinningGestures() {
// Change the cancel pin gesture to home and back if recents button is invisible
- boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
ButtonDispatcher backButton = mView.getBackButton();
ButtonDispatcher recentsButton = mView.getRecentsButton();
- if (pinningActive) {
+ if (mScreenPinningActive) {
boolean recentsVisible = mView.isRecentsButtonVisible();
backButton.setOnLongClickListener(recentsVisible
? this::onLongPressBackRecents
@@ -1227,8 +1250,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
recentsButton.setOnLongClickListener(null);
}
// Note, this needs to be set after even if we're setting the listener to null
- backButton.setLongClickable(pinningActive);
- recentsButton.setLongClickable(pinningActive);
+ backButton.setLongClickable(mScreenPinningActive);
+ recentsButton.setLongClickable(mScreenPinningActive);
}
private void notifyNavigationBarScreenOn() {
@@ -1311,8 +1334,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
@VisibleForTesting
boolean onHomeLongClick(View v) {
- if (!mView.isRecentsButtonVisible()
- && ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
+ if (!mView.isRecentsButtonVisible() && mScreenPinningActive) {
return onLongPressBackHome(v);
}
if (shouldDisableNavbarGestures()) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 891455249867..5d43c5dc19fa 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -57,6 +57,7 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -88,7 +89,6 @@ public class NavigationBarController implements
private FeatureFlags mFeatureFlags;
private final DisplayManager mDisplayManager;
private final TaskbarDelegate mTaskbarDelegate;
- private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private int mNavMode;
@VisibleForTesting boolean mIsTablet;
@@ -112,10 +112,10 @@ public class NavigationBarController implements
NavBarHelper navBarHelper,
TaskbarDelegate taskbarDelegate,
NavigationBarComponent.Factory navigationBarComponentFactory,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
DumpManager dumpManager,
AutoHideController autoHideController,
LightBarController lightBarController,
+ TaskStackChangeListeners taskStackChangeListeners,
Optional<Pip> pipOptional,
Optional<BackAnimation> backAnimation,
FeatureFlags featureFlags) {
@@ -129,11 +129,10 @@ public class NavigationBarController implements
mConfigChanges.applyNewConfig(mContext.getResources());
mNavMode = navigationModeController.addListener(this);
mTaskbarDelegate = taskbarDelegate;
- mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
navBarHelper, navigationModeController, sysUiFlagsContainer,
dumpManager, autoHideController, lightBarController, pipOptional,
- backAnimation.orElse(null));
+ backAnimation.orElse(null), taskStackChangeListeners);
mIsTablet = isTablet(mContext);
dumpManager.registerDumpable(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 403d276f8cbc..88c4fd524b79 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -16,6 +16,7 @@
package com.android.systemui.navigationbar;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.inputmethodservice.InputMethodService.canImeRenderGesturalNavButtons;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
@@ -80,6 +81,7 @@ import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdates
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
@@ -160,6 +162,7 @@ public class NavigationBarView extends FrameLayout {
* fully locked mode we only show that unlocking is blocked.
*/
private ScreenPinningNotify mScreenPinningNotify;
+ private boolean mScreenPinningActive = false;
/**
* {@code true} if the IME can render the back button and the IME switcher button.
@@ -636,14 +639,13 @@ public class NavigationBarView extends FrameLayout {
// When screen pinning, don't hide back and home when connected service or back and
// recents buttons when disconnected from launcher service in screen pinning mode,
// as they are used for exiting.
- final boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
if (mOverviewProxyEnabled) {
// Force disable recents when not in legacy mode
disableRecent |= !QuickStepContract.isLegacyMode(mNavBarMode);
- if (pinningActive && !QuickStepContract.isGesturalMode(mNavBarMode)) {
+ if (mScreenPinningActive && !QuickStepContract.isGesturalMode(mNavBarMode)) {
disableBack = disableHome = false;
}
- } else if (pinningActive) {
+ } else if (mScreenPinningActive) {
disableBack = disableRecent = false;
}
@@ -738,9 +740,7 @@ public class NavigationBarView extends FrameLayout {
public void updateDisabledSystemUiStateFlags(SysUiState sysUiState) {
int displayId = mContext.getDisplayId();
- sysUiState.setFlag(SYSUI_STATE_SCREEN_PINNING,
- ActivityManagerWrapper.getInstance().isScreenPinningActive())
- .setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
+ sysUiState.setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
(mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0)
.setFlag(SYSUI_STATE_HOME_DISABLED,
(mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0)
@@ -749,6 +749,10 @@ public class NavigationBarView extends FrameLayout {
.commitUpdate(displayId);
}
+ public void setInScreenPinning(boolean active) {
+ mScreenPinningActive = active;
+ }
+
private void updatePanelSystemUiStateFlags() {
if (SysUiState.DEBUG) {
Log.d(TAG, "Updating panel sysui state flags: panelView=" + mPanelView);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 5e26e6050eaa..6ee86aa021a3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -16,6 +16,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_SWITCHER_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
@@ -40,6 +41,7 @@ import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode
import android.app.StatusBarManager;
import android.app.StatusBarManager.WindowVisibleState;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -68,6 +70,8 @@ import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -101,6 +105,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private AutoHideController mAutoHideController;
private LightBarController mLightBarController;
private LightBarTransitionsController mLightBarTransitionsController;
+ private TaskStackChangeListeners mTaskStackChangeListeners;
private Optional<Pip> mPipOptional;
private int mDisplayId;
private int mNavigationIconHints;
@@ -127,6 +132,14 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private final DisplayManager mDisplayManager;
private Context mWindowContext;
private ScreenPinningNotify mScreenPinningNotify;
+ private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+ @Override
+ public void onLockTaskModeChanged(int mode) {
+ mSysUiState.setFlag(SYSUI_STATE_SCREEN_PINNING, mode == LOCK_TASK_MODE_PINNED)
+ .commitUpdate(mDisplayId);
+ }
+ };
+
private int mNavigationMode = -1;
private final Consumer<Rect> mPipListener;
@@ -176,7 +189,8 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
AutoHideController autoHideController,
LightBarController lightBarController,
Optional<Pip> pipOptional,
- BackAnimation backAnimation) {
+ BackAnimation backAnimation,
+ TaskStackChangeListeners taskStackChangeListeners) {
// TODO: adding this in the ctor results in a dagger dependency cycle :(
mCommandQueue = commandQueue;
mOverviewProxyService = overviewProxyService;
@@ -189,6 +203,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mPipOptional = pipOptional;
mBackAnimation = backAnimation;
mLightBarTransitionsController = createLightBarTransitionsController();
+ mTaskStackChangeListeners = taskStackChangeListeners;
}
// Separated into a method to keep setDependencies() clean/readable.
@@ -234,6 +249,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mPipOptional.ifPresent(this::addPipExclusionBoundsChangeListener);
mEdgeBackGestureHandler.setBackAnimation(mBackAnimation);
mEdgeBackGestureHandler.onConfigurationChanged(mContext.getResources().getConfiguration());
+ mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
mInitialized = true;
}
@@ -253,6 +269,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mLightBarTransitionsController.destroy();
mLightBarController.setNavigationBar(null);
mPipOptional.ifPresent(this::removePipExclusionBoundsChangeListener);
+ mTaskStackChangeListeners.unregisterTaskStackListener(mTaskStackListener);
mInitialized = false;
}
@@ -300,8 +317,6 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
.setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isWindowVisible())
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
- .setFlag(SYSUI_STATE_SCREEN_PINNING,
- ActivityManagerWrapper.getInstance().isScreenPinningActive())
.setFlag(SYSUI_STATE_IMMERSIVE_MODE, isImmersiveMode())
.commitUpdate(mDisplayId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 13c5b48906c5..eea0e4cd94b9 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -273,7 +273,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
@Override
public void triggerBack() {
// Notify FalsingManager that an intentional gesture has occurred.
- // TODO(b/186519446): use a different method than isFalseTouch
mFalsingManager.isFalseTouch(BACK_GESTURE);
// Only inject back keycodes when ahead-of-time back dispatching is disabled.
if (mBackAnimation == null) {
@@ -502,6 +501,15 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
}
private void updateIsEnabled() {
+ try {
+ Trace.beginSection("EdgeBackGestureHandler#updateIsEnabled");
+ updateIsEnabledTraced();
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ private void updateIsEnabledTraced() {
boolean isEnabled = mIsAttached && mIsGesturalModeEnabled;
if (isEnabled == mIsEnabled) {
return;
@@ -587,13 +595,18 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
}
private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
- if (mEdgeBackPlugin != null) {
- mEdgeBackPlugin.onDestroy();
+ try {
+ Trace.beginSection("setEdgeBackPlugin");
+ if (mEdgeBackPlugin != null) {
+ mEdgeBackPlugin.onDestroy();
+ }
+ mEdgeBackPlugin = edgeBackPlugin;
+ mEdgeBackPlugin.setBackCallback(mBackCallback);
+ mEdgeBackPlugin.setLayoutParams(createLayoutParams());
+ updateDisplaySize();
+ } finally {
+ Trace.endSection();
}
- mEdgeBackPlugin = edgeBackPlugin;
- mEdgeBackPlugin.setBackCallback(mBackCallback);
- mEdgeBackPlugin.setLayoutParams(createLayoutParams());
- updateDisplaySize();
}
public boolean isHandlingGestures() {
@@ -919,6 +932,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mThresholdCrossed = true;
// Capture inputs
mInputMonitor.pilferPointers();
+ if (mBackAnimation != null) {
+ // Notify FalsingManager that an intentional gesture has occurred.
+ mFalsingManager.isFalseTouch(BACK_GESTURE);
+ }
mInputEventReceiver.setBatchingEnabled(true);
} else {
logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_FAR_FROM_EDGE);
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 8356440714e6..08d18575da79 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -104,4 +104,9 @@ constructor(
PackageManager.DONT_KILL_APP,
)
}
+
+ companion object {
+ // TODO(b/254604589): Use final KeyEvent.KEYCODE_* instead.
+ const val NOTE_TASK_KEY_EVENT = 311
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index d14b7a766762..d5f4a5a5d351 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -16,7 +16,6 @@
package com.android.systemui.notetask
-import android.view.KeyEvent
import androidx.annotation.VisibleForTesting
import com.android.systemui.statusbar.CommandQueue
import com.android.wm.shell.bubbles.Bubbles
@@ -37,7 +36,7 @@ constructor(
val callbacks =
object : CommandQueue.Callbacks {
override fun handleSystemKey(keyCode: Int) {
- if (keyCode == KeyEvent.KEYCODE_VIDEO_APP_1) {
+ if (keyCode == NoteTaskController.NOTE_TASK_KEY_EVENT) {
noteTaskController.showNoteTask()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
index 98d69910aac3..26e3f49828c7 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
@@ -21,12 +21,12 @@ import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ResolveInfoFlags
-import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.NOTES_ACTION
+import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.ACTION_CREATE_NOTE
import javax.inject.Inject
/**
- * Class responsible to query all apps and find one that can handle the [NOTES_ACTION]. If found, an
- * [Intent] ready for be launched will be returned. Otherwise, returns null.
+ * Class responsible to query all apps and find one that can handle the [ACTION_CREATE_NOTE]. If
+ * found, an [Intent] ready for be launched will be returned. Otherwise, returns null.
*
* TODO(b/248274123): should be revisited once the notes role is implemented.
*/
@@ -37,15 +37,16 @@ constructor(
) {
fun resolveIntent(): Intent? {
- val intent = Intent(NOTES_ACTION)
+ val intent = Intent(ACTION_CREATE_NOTE)
val flags = ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong())
val infoList = packageManager.queryIntentActivities(intent, flags)
for (info in infoList) {
- val packageName = info.serviceInfo.applicationInfo.packageName ?: continue
+ val packageName = info.activityInfo.applicationInfo.packageName ?: continue
val activityName = resolveActivityNameForNotesAction(packageName) ?: continue
- return Intent(NOTES_ACTION)
+ return Intent(ACTION_CREATE_NOTE)
+ .setPackage(packageName)
.setComponent(ComponentName(packageName, activityName))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
@@ -54,7 +55,7 @@ constructor(
}
private fun resolveActivityNameForNotesAction(packageName: String): String? {
- val intent = Intent(NOTES_ACTION).setPackage(packageName)
+ val intent = Intent(ACTION_CREATE_NOTE).setPackage(packageName)
val flags = ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong())
val resolveInfo = packageManager.resolveActivity(intent, flags)
@@ -69,8 +70,8 @@ constructor(
}
companion object {
- // TODO(b/254606432): Use Intent.ACTION_NOTES and Intent.ACTION_NOTES_LOCKED instead.
- const val NOTES_ACTION = "android.intent.action.NOTES"
+ // TODO(b/254606432): Use Intent.ACTION_CREATE_NOTE instead.
+ const val ACTION_CREATE_NOTE = "android.intent.action.CREATE_NOTE"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
index 47fe67638cd0..f203e7a51643 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
@@ -45,8 +45,8 @@ constructor(
fun newIntent(context: Context): Intent {
return Intent(context, LaunchNoteTaskActivity::class.java).apply {
// Intent's action must be set in shortcuts, or an exception will be thrown.
- // TODO(b/254606432): Use Intent.ACTION_NOTES instead.
- action = NoteTaskIntentResolver.NOTES_ACTION
+ // TODO(b/254606432): Use Intent.ACTION_CREATE_NOTE instead.
+ action = NoteTaskIntentResolver.ACTION_CREATE_NOTE
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index fba5f63ea9c7..7f0f89415280 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -68,8 +68,10 @@ public class PeopleSpaceActivity extends ComponentActivity {
};
if (ComposeFacade.INSTANCE.isComposeAvailable()) {
+ Log.d(TAG, "Using the Compose implementation of the PeopleSpaceActivity");
ComposeFacade.INSTANCE.setPeopleSpaceActivityContent(this, viewModel, onResult);
} else {
+ Log.d(TAG, "Using the View implementation of the PeopleSpaceActivity");
ViewGroup view = PeopleViewBinder.create(this);
PeopleViewBinder.bind(view, viewModel, /* lifecycleOwner= */ this, onResult);
setContentView(view);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 774cb3442811..8ad102ece9b3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -49,6 +49,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.ShadeInterpolation;
+import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.controls.ui.MediaHost;
@@ -228,9 +229,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mQSFooterActionsViewModel = mFooterActionsViewModelFactory.create(/* lifecycleOwner */
this);
- LinearLayout footerActionsView = view.findViewById(R.id.qs_footer_actions);
- FooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel,
- mListeningAndVisibilityLifecycleOwner);
+ bindFooterActionsView(view);
mFooterActionsController.init();
mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view);
@@ -291,6 +290,33 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
});
}
+ private void bindFooterActionsView(View root) {
+ LinearLayout footerActionsView = root.findViewById(R.id.qs_footer_actions);
+
+ if (!ComposeFacade.INSTANCE.isComposeAvailable()) {
+ Log.d(TAG, "Binding the View implementation of the QS footer actions");
+ FooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel,
+ mListeningAndVisibilityLifecycleOwner);
+ return;
+ }
+
+ // Compose is available, so let's use the Compose implementation of the footer actions.
+ Log.d(TAG, "Binding the Compose implementation of the QS footer actions");
+ View composeView = ComposeFacade.INSTANCE.createFooterActionsView(root.getContext(),
+ mQSFooterActionsViewModel, mListeningAndVisibilityLifecycleOwner);
+
+ // The id R.id.qs_footer_actions is used by QSContainerImpl to set the horizontal margin
+ // to all views except for qs_footer_actions, so we set it to the Compose view.
+ composeView.setId(R.id.qs_footer_actions);
+
+ // Replace the View by the Compose provided one.
+ ViewGroup parent = (ViewGroup) footerActionsView.getParent();
+ ViewGroup.LayoutParams layoutParams = footerActionsView.getLayoutParams();
+ int index = parent.indexOfChild(footerActionsView);
+ parent.removeViewAt(index);
+ parent.addView(composeView, index, layoutParams);
+ }
+
@Override
public void setScrollListener(ScrollListener listener) {
mScrollListener = listener;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 3d48fd109e39..84a18d8dd365 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -132,7 +132,7 @@ public class TileServices extends IQSService.Stub {
final String slot = tile.getComponent().getClassName();
// TileServices doesn't know how to add more than 1 icon per slot, so remove all
mMainHandler.post(() -> mHost.getIconController()
- .removeAllIconsForSlot(slot));
+ .removeAllIconsForExternalSlot(slot));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
index 30f81243e8d0..19215867e678 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
@@ -219,9 +219,9 @@ object FooterActionsViewBinder {
// Small button with the number only.
foregroundServicesWithTextView.isVisible = false
- foregroundServicesWithNumberView.visibility = View.VISIBLE
+ foregroundServicesWithNumberView.isVisible = true
foregroundServicesWithNumberView.setOnClickListener {
- foregroundServices.onClick(Expandable.fromView(foregroundServicesWithTextView))
+ foregroundServices.onClick(Expandable.fromView(foregroundServicesWithNumberView))
}
foregroundServicesWithNumberHolder.number.text = foregroundServicesCount.toString()
foregroundServicesWithNumberHolder.number.contentDescription = foregroundServices.text
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index b355d4bb67fe..29d7fb02e613 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -145,7 +145,6 @@ open class QSTileViewImpl @JvmOverloads constructor(
private val launchableViewDelegate = LaunchableViewDelegate(
this,
superSetVisibility = { super.setVisibility(it) },
- superSetTransitionVisibility = { super.setTransitionVisibility(it) },
)
private var lastDisabledByPolicy = false
@@ -362,10 +361,6 @@ open class QSTileViewImpl @JvmOverloads constructor(
launchableViewDelegate.setVisibility(visibility)
}
- override fun setTransitionVisibility(visibility: Int) {
- launchableViewDelegate.setTransitionVisibility(visibility)
- }
-
// Accessibility
override fun onInitializeAccessibilityEvent(event: AccessibilityEvent) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index 44b18ec4639b..68e3dcdb63ea 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -23,6 +23,7 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.ResultReceiver
+import android.os.UserHandle
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
@@ -77,6 +78,14 @@ class ScreenRecordPermissionDialog(
MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
CaptureTargetResultReceiver()
)
+
+ // Send SystemUI's user handle as the host app user handle because SystemUI
+ // is the 'host app' (the app that receives screen capture data)
+ intent.putExtra(
+ MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
+ UserHandle.of(UserHandle.myUserId())
+ )
+
val animationController = dialogLaunchAnimator.createActivityLaunchController(v!!)
if (animationController == null) {
dismiss()
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index b21a4857c736..4f94ed18e1ef 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -553,6 +553,10 @@ public class ScreenshotController {
Log.d(TAG, "adding OnComputeInternalInsetsListener");
}
mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(mScreenshotView);
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "setContentView: " + mScreenshotView);
+ }
+ setContentView(mScreenshotView);
}
/**
@@ -634,6 +638,7 @@ public class ScreenshotController {
// The window is focusable by default
setWindowFocusable(true);
+ mScreenshotView.requestFocus();
// Wait until this window is attached to request because it is
// the reference used to locate the target window (below).
@@ -691,10 +696,7 @@ public class ScreenshotController {
mContext.getDrawable(R.drawable.overlay_badge_background), owner));
}
mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
- if (DEBUG_WINDOW) {
- Log.d(TAG, "setContentView: " + mScreenshotView);
- }
- setContentView(mScreenshotView);
+
// ignore system bar insets for the purpose of window layout
mWindow.getDecorView().setOnApplyWindowInsetsListener(
(v, insets) -> WindowInsets.CONSUMED);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
index 5011227ad2cc..b3d31f2986d1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
@@ -69,7 +69,8 @@ object CombinedShadeHeadersConstraintManagerImpl : CombinedShadeHeadersConstrain
}
return ConstraintsChanges(
qqsConstraintsChanges = change,
- qsConstraintsChanges = change
+ qsConstraintsChanges = change,
+ largeScreenConstraintsChanges = change,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index e406be1ea0a3..88676371bd6b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -113,7 +113,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
QQS_HEADER_CONSTRAINT -> "QQS Header"
QS_HEADER_CONSTRAINT -> "QS Header"
LARGE_SCREEN_HEADER_CONSTRAINT -> "Large Screen Header"
- else -> "Unknown state"
+ else -> "Unknown state $this"
}
}
@@ -296,6 +296,9 @@ class LargeScreenShadeHeaderController @Inject constructor(
override fun onViewAttached() {
privacyIconsController.chipVisibilityListener = chipVisibilityListener
+ updateVisibility()
+ updateTransition()
+
if (header is MotionLayout) {
header.setOnApplyWindowInsetsListener(insetListener)
clock.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
@@ -308,9 +311,6 @@ class LargeScreenShadeHeaderController @Inject constructor(
dumpManager.registerDumpable(this)
configurationController.addCallback(configurationControllerListener)
demoModeController.addCallback(demoModeReceiver)
-
- updateVisibility()
- updateTransition()
}
override fun onViewDetached() {
@@ -436,15 +436,14 @@ class LargeScreenShadeHeaderController @Inject constructor(
header as MotionLayout
if (largeScreenActive) {
logInstantEvent("Large screen constraints set")
- header.setTransition(HEADER_TRANSITION_ID)
- header.transitionToStart()
+ header.setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID)
} else {
logInstantEvent("Small screen constraints set")
header.setTransition(HEADER_TRANSITION_ID)
- header.transitionToStart()
- updatePosition()
- updateScrollY()
}
+ header.jumpToState(header.startState)
+ updatePosition()
+ updateScrollY()
}
private fun updatePosition() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index ccffc5f14559..ce6fb14fbcf8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -144,6 +144,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
@@ -449,6 +450,7 @@ public final class NotificationPanelViewController implements Dumpable {
private float mDownY;
private int mDisplayTopInset = 0; // in pixels
private int mDisplayRightInset = 0; // in pixels
+ private int mDisplayLeftInset = 0; // in pixels
private int mLargeScreenShadeHeaderHeight;
private int mSplitShadeNotificationsScrimMarginBottom;
@@ -692,6 +694,7 @@ public final class NotificationPanelViewController implements Dumpable {
private DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel;
private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
+ private GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -700,6 +703,7 @@ public final class NotificationPanelViewController implements Dumpable {
private int mDreamingToLockscreenTransitionTranslationY;
private int mOccludedToLockscreenTransitionTranslationY;
private int mLockscreenToDreamingTransitionTranslationY;
+ private int mGoneToDreamingTransitionTranslationY;
private int mLockscreenToOccludedTransitionTranslationY;
private boolean mUnocclusionTransitionFlagEnabled = false;
@@ -712,7 +716,7 @@ public final class NotificationPanelViewController implements Dumpable {
updatePanelExpansionAndVisibility();
};
private final Runnable mMaybeHideExpandedRunnable = () -> {
- if (getExpansionFraction() == 0.0f) {
+ if (getExpandedFraction() == 0.0f) {
postToView(mHideExpandedRunnable);
}
};
@@ -735,6 +739,12 @@ public final class NotificationPanelViewController implements Dumpable {
step.getTransitionState() == TransitionState.RUNNING;
};
+ private final Consumer<TransitionStep> mGoneToDreamingTransition =
+ (TransitionStep step) -> {
+ mIsOcclusionTransitionRunning =
+ step.getTransitionState() == TransitionState.RUNNING;
+ };
+
private final Consumer<TransitionStep> mLockscreenToOccludedTransition =
(TransitionStep step) -> {
mIsOcclusionTransitionRunning =
@@ -813,6 +823,7 @@ public final class NotificationPanelViewController implements Dumpable {
DreamingToLockscreenTransitionViewModel dreamingToLockscreenTransitionViewModel,
OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel,
LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel,
+ GoneToDreamingTransitionViewModel goneToDreamingTransitionViewModel,
LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel,
@Main CoroutineDispatcher mainDispatcher,
KeyguardTransitionInteractor keyguardTransitionInteractor,
@@ -834,6 +845,7 @@ public final class NotificationPanelViewController implements Dumpable {
mDreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel;
mOccludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel;
mLockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel;
+ mGoneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel;
mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@@ -1172,6 +1184,17 @@ public final class NotificationPanelViewController implements Dumpable {
setTransitionY(mNotificationStackScrollLayoutController),
mMainDispatcher);
+ // Gone->Dreaming
+ collectFlow(mView, mKeyguardTransitionInteractor.getGoneToDreamingTransition(),
+ mGoneToDreamingTransition, mMainDispatcher);
+ collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(),
+ setTransitionAlpha(mNotificationStackScrollLayoutController),
+ mMainDispatcher);
+ collectFlow(mView, mGoneToDreamingTransitionViewModel.lockscreenTranslationY(
+ mGoneToDreamingTransitionTranslationY),
+ setTransitionY(mNotificationStackScrollLayoutController),
+ mMainDispatcher);
+
// Lockscreen->Occluded
collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(),
mLockscreenToOccludedTransition, mMainDispatcher);
@@ -1223,6 +1246,8 @@ public final class NotificationPanelViewController implements Dumpable {
R.dimen.occluded_to_lockscreen_transition_lockscreen_translation_y);
mLockscreenToDreamingTransitionTranslationY = mResources.getDimensionPixelSize(
R.dimen.lockscreen_to_dreaming_transition_lockscreen_translation_y);
+ mGoneToDreamingTransitionTranslationY = mResources.getDimensionPixelSize(
+ R.dimen.gone_to_dreaming_transition_lockscreen_translation_y);
mLockscreenToOccludedTransitionTranslationY = mResources.getDimensionPixelSize(
R.dimen.lockscreen_to_occluded_transition_lockscreen_translation_y);
}
@@ -2473,7 +2498,7 @@ public final class NotificationPanelViewController implements Dumpable {
mInitialTouchY = event.getY();
mInitialTouchX = event.getX();
}
- if (!isFullyCollapsed()) {
+ if (!isFullyCollapsed() && !isShadeOrQsHeightAnimationRunning()) {
handleQsDown(event);
}
// defer touches on QQS to shade while shade is collapsing. Added margin for error
@@ -2987,7 +3012,7 @@ public final class NotificationPanelViewController implements Dumpable {
// left bounds can ignore insets, it should always reach the edge of the screen
return 0;
} else {
- return mNotificationStackScrollLayoutController.getLeft();
+ return mNotificationStackScrollLayoutController.getLeft() + mDisplayLeftInset;
}
}
@@ -2995,7 +3020,7 @@ public final class NotificationPanelViewController implements Dumpable {
if (mIsFullWidth) {
return mView.getRight() + mDisplayRightInset;
} else {
- return mNotificationStackScrollLayoutController.getRight();
+ return mNotificationStackScrollLayoutController.getRight() + mDisplayLeftInset;
}
}
@@ -3119,8 +3144,8 @@ public final class NotificationPanelViewController implements Dumpable {
// Convert global clipping coordinates to local ones,
// relative to NotificationStackScrollLayout
- int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft();
- int nsslRight = right - mNotificationStackScrollLayoutController.getLeft();
+ int nsslLeft = calculateNsslLeft(left);
+ int nsslRight = calculateNsslRight(right);
int nsslTop = getNotificationsClippingTopBounds(top);
int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop();
int bottomRadius = mSplitShadeEnabled ? radius : 0;
@@ -3129,6 +3154,22 @@ public final class NotificationPanelViewController implements Dumpable {
nsslLeft, nsslTop, nsslRight, nsslBottom, topRadius, bottomRadius);
}
+ private int calculateNsslLeft(int nsslLeftAbsolute) {
+ int left = nsslLeftAbsolute - mNotificationStackScrollLayoutController.getLeft();
+ if (mIsFullWidth) {
+ return left;
+ }
+ return left - mDisplayLeftInset;
+ }
+
+ private int calculateNsslRight(int nsslRightAbsolute) {
+ int right = nsslRightAbsolute - mNotificationStackScrollLayoutController.getLeft();
+ if (mIsFullWidth) {
+ return right;
+ }
+ return right - mDisplayLeftInset;
+ }
+
private int getNotificationsClippingTopBounds(int qsTop) {
if (mSplitShadeEnabled && mExpandingFromHeadsUp) {
// in split shade nssl has extra top margin so clipping at top 0 is not enough, we need
@@ -4526,6 +4567,7 @@ public final class NotificationPanelViewController implements Dumpable {
ipw.print("mDownY="); ipw.println(mDownY);
ipw.print("mDisplayTopInset="); ipw.println(mDisplayTopInset);
ipw.print("mDisplayRightInset="); ipw.println(mDisplayRightInset);
+ ipw.print("mDisplayLeftInset="); ipw.println(mDisplayLeftInset);
ipw.print("mLargeScreenShadeHeaderHeight="); ipw.println(mLargeScreenShadeHeaderHeight);
ipw.print("mSplitShadeNotificationsScrimMarginBottom=");
ipw.println(mSplitShadeNotificationsScrimMarginBottom);
@@ -5263,6 +5305,11 @@ public final class NotificationPanelViewController implements Dumpable {
}
}
+ /** Returns whether a shade or QS expansion animation is running */
+ private boolean isShadeOrQsHeightAnimationRunning() {
+ return mHeightAnimator != null && !mHintAnimationRunning && !mIsSpringBackAnimation;
+ }
+
/**
* Phase 2: Bounce down.
*/
@@ -5422,10 +5469,6 @@ public final class NotificationPanelViewController implements Dumpable {
InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
}
- private float getExpansionFraction() {
- return mExpandedFraction;
- }
-
private ShadeExpansionStateManager getShadeExpansionStateManager() {
return mShadeExpansionStateManager;
}
@@ -5902,6 +5945,7 @@ public final class NotificationPanelViewController implements Dumpable {
Insets combinedInsets = insets.getInsetsIgnoringVisibility(insetTypes);
mDisplayTopInset = combinedInsets.top;
mDisplayRightInset = combinedInsets.right;
+ mDisplayLeftInset = combinedInsets.left;
mNavigationBarBottomHeight = insets.getStableInsetBottom();
updateMaxHeadsUpTranslation();
@@ -6280,8 +6324,7 @@ public final class NotificationPanelViewController implements Dumpable {
mCollapsedAndHeadsUpOnDown =
isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp();
addMovement(event);
- boolean regularHeightAnimationRunning = mHeightAnimator != null
- && !mHintAnimationRunning && !mIsSpringBackAnimation;
+ boolean regularHeightAnimationRunning = isShadeOrQsHeightAnimationRunning();
if (!mGestureWaitForTouchSlop || regularHeightAnimationRunning) {
mTouchSlopExceeded = regularHeightAnimationRunning
|| mTouchSlopExceededBeforeDown;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 8314ec713ccb..ab2e692915ad 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -19,6 +19,7 @@ package com.android.systemui.shade;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
@@ -52,13 +53,13 @@ import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.dump.DumpsysTableLogger;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
@@ -72,10 +73,8 @@ import java.io.PrintWriter;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -89,6 +88,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
Dumpable, ConfigurationListener {
private static final String TAG = "NotificationShadeWindowController";
+ private static final int MAX_STATE_CHANGES_BUFFER_SIZE = 100;
private final Context mContext;
private final WindowManager mWindowManager;
@@ -108,7 +108,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
private boolean mHasTopUi;
private boolean mHasTopUiChanged;
private float mScreenBrightnessDoze;
- private final State mCurrentState = new State();
+ private final NotificationShadeWindowState mCurrentState = new NotificationShadeWindowState();
private OtherwisedCollapsedListener mListener;
private ForcePluginOpenListener mForcePluginOpenListener;
private Consumer<Integer> mScrimsVisibilityListener;
@@ -125,6 +125,9 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
private int mDeferWindowLayoutParams;
private boolean mLastKeyguardRotationAllowed;
+ private final NotificationShadeWindowState.Buffer mStateBuffer =
+ new NotificationShadeWindowState.Buffer(MAX_STATE_CHANGES_BUFFER_SIZE);
+
@Inject
public NotificationShadeWindowControllerImpl(Context context, WindowManager windowManager,
IActivityManager activityManager, DozeParameters dozeParameters,
@@ -210,8 +213,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
@VisibleForTesting
void onShadeExpansionFullyChanged(Boolean isExpanded) {
- if (mCurrentState.mPanelExpanded != isExpanded) {
- mCurrentState.mPanelExpanded = isExpanded;
+ if (mCurrentState.panelExpanded != isExpanded) {
+ mCurrentState.panelExpanded = isExpanded;
apply(mCurrentState);
}
}
@@ -251,6 +254,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
mLp.setTitle("NotificationShade");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mLp.privateFlags |= PRIVATE_FLAG_OPTIMIZE_MEASURE;
// We use BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE here, however, there is special logic in
// window manager which disables the transient show behavior.
@@ -296,10 +300,10 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
mNotificationShadeView.setSystemUiVisibility(vis);
}
- private void applyKeyguardFlags(State state) {
- final boolean keyguardOrAod = state.mKeyguardShowing
- || (state.mDozing && mDozeParameters.getAlwaysOn());
- if ((keyguardOrAod && !state.mBackdropShowing && !state.mLightRevealScrimOpaque)
+ private void applyKeyguardFlags(NotificationShadeWindowState state) {
+ final boolean keyguardOrAod = state.keyguardShowing
+ || (state.dozing && mDozeParameters.getAlwaysOn());
+ if ((keyguardOrAod && !state.mediaBackdropShowing && !state.lightRevealScrimOpaque)
|| mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehind()) {
// Show the wallpaper if we're on keyguard/AOD and the wallpaper is not occluded by a
// solid backdrop. Also, show it if we are currently animating between the
@@ -310,28 +314,31 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
mLpChanged.flags &= ~LayoutParams.FLAG_SHOW_WALLPAPER;
}
- if (state.mDozing) {
+ if (state.dozing) {
mLpChanged.privateFlags |= LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
} else {
mLpChanged.privateFlags &= ~LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
}
if (mKeyguardPreferredRefreshRate > 0) {
- boolean onKeyguard = state.mStatusBarState == StatusBarState.KEYGUARD
- && !state.mKeyguardFadingAway && !state.mKeyguardGoingAway;
+ boolean onKeyguard = state.statusBarState == StatusBarState.KEYGUARD
+ && !state.keyguardFadingAway && !state.keyguardGoingAway;
if (onKeyguard
&& mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())) {
+ // both max and min display refresh rate must be set to take effect:
mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardPreferredRefreshRate;
+ mLpChanged.preferredMinDisplayRefreshRate = mKeyguardPreferredRefreshRate;
} else {
mLpChanged.preferredMaxDisplayRefreshRate = 0;
+ mLpChanged.preferredMinDisplayRefreshRate = 0;
}
Trace.setCounter("display_set_preferred_refresh_rate",
(long) mKeyguardPreferredRefreshRate);
} else if (mKeyguardMaxRefreshRate > 0) {
boolean bypassOnKeyguard = mKeyguardBypassController.getBypassEnabled()
- && state.mStatusBarState == StatusBarState.KEYGUARD
- && !state.mKeyguardFadingAway && !state.mKeyguardGoingAway;
- if (state.mDozing || bypassOnKeyguard) {
+ && state.statusBarState == StatusBarState.KEYGUARD
+ && !state.keyguardFadingAway && !state.keyguardGoingAway;
+ if (state.dozing || bypassOnKeyguard) {
mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardMaxRefreshRate;
} else {
mLpChanged.preferredMaxDisplayRefreshRate = 0;
@@ -340,7 +347,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
(long) mLpChanged.preferredMaxDisplayRefreshRate);
}
- if (state.mBouncerShowing && !isDebuggable()) {
+ if (state.bouncerShowing && !isDebuggable()) {
mLpChanged.flags |= LayoutParams.FLAG_SECURE;
} else {
mLpChanged.flags &= ~LayoutParams.FLAG_SECURE;
@@ -351,8 +358,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
return Build.IS_DEBUGGABLE;
}
- private void adjustScreenOrientation(State state) {
- if (state.mBouncerShowing || state.isKeyguardShowingAndNotOccluded() || state.mDozing) {
+ private void adjustScreenOrientation(NotificationShadeWindowState state) {
+ if (state.bouncerShowing || state.isKeyguardShowingAndNotOccluded() || state.dozing) {
if (mKeyguardStateController.isKeyguardScreenRotationAllowed()) {
mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
} else {
@@ -363,10 +370,10 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
}
}
- private void applyFocusableFlag(State state) {
- boolean panelFocusable = state.mNotificationShadeFocusable && state.mPanelExpanded;
- if (state.mBouncerShowing && (state.mKeyguardOccluded || state.mKeyguardNeedsInput)
- || ENABLE_REMOTE_INPUT && state.mRemoteInputActive
+ private void applyFocusableFlag(NotificationShadeWindowState state) {
+ boolean panelFocusable = state.notificationShadeFocusable && state.panelExpanded;
+ if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput)
+ || ENABLE_REMOTE_INPUT && state.remoteInputActive
// Make the panel focusable if we're doing the screen off animation, since the light
// reveal scrim is drawing in the panel and should consume touch events so that they
// don't go to the app behind.
@@ -376,7 +383,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
} else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
// Make sure to remove FLAG_ALT_FOCUSABLE_IM when keyguard needs input.
- if (state.mKeyguardNeedsInput && state.isKeyguardShowingAndNotOccluded()) {
+ if (state.keyguardNeedsInput && state.isKeyguardShowingAndNotOccluded()) {
mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else {
mLpChanged.flags |= LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -387,19 +394,19 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
}
}
- private void applyForceShowNavigationFlag(State state) {
- if (state.mPanelExpanded || state.mBouncerShowing
- || ENABLE_REMOTE_INPUT && state.mRemoteInputActive) {
+ private void applyForceShowNavigationFlag(NotificationShadeWindowState state) {
+ if (state.panelExpanded || state.bouncerShowing
+ || ENABLE_REMOTE_INPUT && state.remoteInputActive) {
mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
} else {
mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
}
}
- private void applyVisibility(State state) {
+ private void applyVisibility(NotificationShadeWindowState state) {
boolean visible = isExpanded(state);
mLogger.logApplyVisibility(visible);
- if (state.mForcePluginOpen) {
+ if (state.forcePluginOpen) {
if (mListener != null) {
mListener.setWouldOtherwiseCollapse(visible);
}
@@ -415,16 +422,16 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
}
}
- private boolean isExpanded(State state) {
- return !state.mForceCollapsed && (state.isKeyguardShowingAndNotOccluded()
- || state.mPanelVisible || state.mKeyguardFadingAway || state.mBouncerShowing
- || state.mHeadsUpShowing
- || state.mScrimsVisibility != ScrimController.TRANSPARENT)
- || state.mBackgroundBlurRadius > 0
- || state.mLaunchingActivity;
+ private boolean isExpanded(NotificationShadeWindowState state) {
+ return !state.forceWindowCollapsed && (state.isKeyguardShowingAndNotOccluded()
+ || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
+ || state.headsUpNotificationShowing
+ || state.scrimsVisibility != ScrimController.TRANSPARENT)
+ || state.backgroundBlurRadius > 0
+ || state.launchingActivityFromNotification;
}
- private void applyFitsSystemWindows(State state) {
+ private void applyFitsSystemWindows(NotificationShadeWindowState state) {
boolean fitsSystemWindows = !state.isKeyguardShowingAndNotOccluded();
if (mNotificationShadeView != null
&& mNotificationShadeView.getFitsSystemWindows() != fitsSystemWindows) {
@@ -433,21 +440,21 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
}
}
- private void applyUserActivityTimeout(State state) {
+ private void applyUserActivityTimeout(NotificationShadeWindowState state) {
if (state.isKeyguardShowingAndNotOccluded()
- && state.mStatusBarState == StatusBarState.KEYGUARD
- && !state.mQsExpanded) {
- mLpChanged.userActivityTimeout = state.mBouncerShowing
+ && state.statusBarState == StatusBarState.KEYGUARD
+ && !state.qsExpanded) {
+ mLpChanged.userActivityTimeout = state.bouncerShowing
? KeyguardViewMediator.AWAKE_INTERVAL_BOUNCER_MS : mLockScreenDisplayTimeout;
} else {
mLpChanged.userActivityTimeout = -1;
}
}
- private void applyInputFeatures(State state) {
+ private void applyInputFeatures(NotificationShadeWindowState state) {
if (state.isKeyguardShowingAndNotOccluded()
- && state.mStatusBarState == StatusBarState.KEYGUARD
- && !state.mQsExpanded && !state.mForceUserActivity) {
+ && state.statusBarState == StatusBarState.KEYGUARD
+ && !state.qsExpanded && !state.forceUserActivity) {
mLpChanged.inputFeatures |=
LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
} else {
@@ -456,7 +463,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
}
}
- private void applyStatusBarColorSpaceAgnosticFlag(State state) {
+ private void applyStatusBarColorSpaceAgnosticFlag(NotificationShadeWindowState state) {
if (!isExpanded(state)) {
mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
} else {
@@ -482,8 +489,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
applyWindowLayoutParams();
}
- private void apply(State state) {
- mLogger.logNewState(state);
+ private void apply(NotificationShadeWindowState state) {
+ logState(state);
applyKeyguardFlags(state);
applyFocusableFlag(state);
applyForceShowNavigationFlag(state);
@@ -512,6 +519,38 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
notifyStateChangedCallbacks();
}
+ private void logState(NotificationShadeWindowState state) {
+ mStateBuffer.insert(
+ state.keyguardShowing,
+ state.keyguardOccluded,
+ state.keyguardNeedsInput,
+ state.panelVisible,
+ state.panelExpanded,
+ state.notificationShadeFocusable,
+ state.bouncerShowing,
+ state.keyguardFadingAway,
+ state.keyguardGoingAway,
+ state.qsExpanded,
+ state.headsUpNotificationShowing,
+ state.lightRevealScrimOpaque,
+ state.forceWindowCollapsed,
+ state.forceDozeBrightness,
+ state.forceUserActivity,
+ state.launchingActivityFromNotification,
+ state.mediaBackdropShowing,
+ state.wallpaperSupportsAmbientMode,
+ state.windowNotTouchable,
+ state.componentsForcingTopUi,
+ state.forceOpenTokens,
+ state.statusBarState,
+ state.remoteInputActive,
+ state.forcePluginOpen,
+ state.dozing,
+ state.scrimsVisibility,
+ state.backgroundBlurRadius
+ );
+ }
+
@Override
public void notifyStateChangedCallbacks() {
// Copy callbacks to separate ArrayList to avoid concurrent modification
@@ -520,36 +559,36 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
.filter(Objects::nonNull)
.collect(Collectors.toList());
for (StatusBarWindowCallback cb : activeCallbacks) {
- cb.onStateChanged(mCurrentState.mKeyguardShowing,
- mCurrentState.mKeyguardOccluded,
- mCurrentState.mBouncerShowing,
- mCurrentState.mDozing,
- mCurrentState.mPanelExpanded);
+ cb.onStateChanged(mCurrentState.keyguardShowing,
+ mCurrentState.keyguardOccluded,
+ mCurrentState.bouncerShowing,
+ mCurrentState.dozing,
+ mCurrentState.panelExpanded);
}
}
- private void applyModalFlag(State state) {
- if (state.mHeadsUpShowing) {
+ private void applyModalFlag(NotificationShadeWindowState state) {
+ if (state.headsUpNotificationShowing) {
mLpChanged.flags |= LayoutParams.FLAG_NOT_TOUCH_MODAL;
} else {
mLpChanged.flags &= ~LayoutParams.FLAG_NOT_TOUCH_MODAL;
}
}
- private void applyBrightness(State state) {
- if (state.mForceDozeBrightness) {
+ private void applyBrightness(NotificationShadeWindowState state) {
+ if (state.forceDozeBrightness) {
mLpChanged.screenBrightness = mScreenBrightnessDoze;
} else {
mLpChanged.screenBrightness = LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
}
}
- private void applyHasTopUi(State state) {
- mHasTopUiChanged = !state.mComponentsForcingTopUi.isEmpty() || isExpanded(state);
+ private void applyHasTopUi(NotificationShadeWindowState state) {
+ mHasTopUiChanged = !state.componentsForcingTopUi.isEmpty() || isExpanded(state);
}
- private void applyNotTouchable(State state) {
- if (state.mNotTouchable) {
+ private void applyNotTouchable(NotificationShadeWindowState state) {
+ if (state.windowNotTouchable) {
mLpChanged.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
} else {
mLpChanged.flags &= ~LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -571,88 +610,88 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
@Override
public void setKeyguardShowing(boolean showing) {
- mCurrentState.mKeyguardShowing = showing;
+ mCurrentState.keyguardShowing = showing;
apply(mCurrentState);
}
@Override
public void setKeyguardOccluded(boolean occluded) {
- mCurrentState.mKeyguardOccluded = occluded;
+ mCurrentState.keyguardOccluded = occluded;
apply(mCurrentState);
}
@Override
public void setKeyguardNeedsInput(boolean needsInput) {
- mCurrentState.mKeyguardNeedsInput = needsInput;
+ mCurrentState.keyguardNeedsInput = needsInput;
apply(mCurrentState);
}
@Override
public void setPanelVisible(boolean visible) {
- if (mCurrentState.mPanelVisible == visible
- && mCurrentState.mNotificationShadeFocusable == visible) {
+ if (mCurrentState.panelVisible == visible
+ && mCurrentState.notificationShadeFocusable == visible) {
return;
}
mLogger.logShadeVisibleAndFocusable(visible);
- mCurrentState.mPanelVisible = visible;
- mCurrentState.mNotificationShadeFocusable = visible;
+ mCurrentState.panelVisible = visible;
+ mCurrentState.notificationShadeFocusable = visible;
apply(mCurrentState);
}
@Override
public void setNotificationShadeFocusable(boolean focusable) {
mLogger.logShadeFocusable(focusable);
- mCurrentState.mNotificationShadeFocusable = focusable;
+ mCurrentState.notificationShadeFocusable = focusable;
apply(mCurrentState);
}
@Override
public void setBouncerShowing(boolean showing) {
- mCurrentState.mBouncerShowing = showing;
+ mCurrentState.bouncerShowing = showing;
apply(mCurrentState);
}
@Override
public void setBackdropShowing(boolean showing) {
- mCurrentState.mBackdropShowing = showing;
+ mCurrentState.mediaBackdropShowing = showing;
apply(mCurrentState);
}
@Override
public void setKeyguardFadingAway(boolean keyguardFadingAway) {
- mCurrentState.mKeyguardFadingAway = keyguardFadingAway;
+ mCurrentState.keyguardFadingAway = keyguardFadingAway;
apply(mCurrentState);
}
private void onQsExpansionChanged(Boolean expanded) {
- mCurrentState.mQsExpanded = expanded;
+ mCurrentState.qsExpanded = expanded;
apply(mCurrentState);
}
@Override
public void setForceUserActivity(boolean forceUserActivity) {
- mCurrentState.mForceUserActivity = forceUserActivity;
+ mCurrentState.forceUserActivity = forceUserActivity;
apply(mCurrentState);
}
@Override
public void setLaunchingActivity(boolean launching) {
- mCurrentState.mLaunchingActivity = launching;
+ mCurrentState.launchingActivityFromNotification = launching;
apply(mCurrentState);
}
@Override
public boolean isLaunchingActivity() {
- return mCurrentState.mLaunchingActivity;
+ return mCurrentState.launchingActivityFromNotification;
}
@Override
public void setScrimsVisibility(int scrimsVisibility) {
- if (scrimsVisibility == mCurrentState.mScrimsVisibility) {
+ if (scrimsVisibility == mCurrentState.scrimsVisibility) {
return;
}
boolean wasExpanded = isExpanded(mCurrentState);
- mCurrentState.mScrimsVisibility = scrimsVisibility;
+ mCurrentState.scrimsVisibility = scrimsVisibility;
if (wasExpanded != isExpanded(mCurrentState)) {
apply(mCurrentState);
}
@@ -666,31 +705,31 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
*/
@Override
public void setBackgroundBlurRadius(int backgroundBlurRadius) {
- if (mCurrentState.mBackgroundBlurRadius == backgroundBlurRadius) {
+ if (mCurrentState.backgroundBlurRadius == backgroundBlurRadius) {
return;
}
- mCurrentState.mBackgroundBlurRadius = backgroundBlurRadius;
+ mCurrentState.backgroundBlurRadius = backgroundBlurRadius;
apply(mCurrentState);
}
@Override
public void setHeadsUpShowing(boolean showing) {
- mCurrentState.mHeadsUpShowing = showing;
+ mCurrentState.headsUpNotificationShowing = showing;
apply(mCurrentState);
}
@Override
public void setLightRevealScrimOpaque(boolean opaque) {
- if (mCurrentState.mLightRevealScrimOpaque == opaque) {
+ if (mCurrentState.lightRevealScrimOpaque == opaque) {
return;
}
- mCurrentState.mLightRevealScrimOpaque = opaque;
+ mCurrentState.lightRevealScrimOpaque = opaque;
apply(mCurrentState);
}
@Override
public void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
- mCurrentState.mWallpaperSupportsAmbientMode = supportsAmbientMode;
+ mCurrentState.wallpaperSupportsAmbientMode = supportsAmbientMode;
apply(mCurrentState);
}
@@ -698,7 +737,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
* @param state The {@link StatusBarStateController} of the status bar.
*/
private void setStatusBarState(int state) {
- mCurrentState.mStatusBarState = state;
+ mCurrentState.statusBarState = state;
apply(mCurrentState);
}
@@ -709,13 +748,13 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
*/
@Override
public void setForceWindowCollapsed(boolean force) {
- mCurrentState.mForceCollapsed = force;
+ mCurrentState.forceWindowCollapsed = force;
apply(mCurrentState);
}
@Override
public void onRemoteInputActive(boolean remoteInputActive) {
- mCurrentState.mRemoteInputActive = remoteInputActive;
+ mCurrentState.remoteInputActive = remoteInputActive;
apply(mCurrentState);
}
@@ -725,32 +764,32 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
*/
@Override
public void setForceDozeBrightness(boolean forceDozeBrightness) {
- if (mCurrentState.mForceDozeBrightness == forceDozeBrightness) {
+ if (mCurrentState.forceDozeBrightness == forceDozeBrightness) {
return;
}
- mCurrentState.mForceDozeBrightness = forceDozeBrightness;
+ mCurrentState.forceDozeBrightness = forceDozeBrightness;
apply(mCurrentState);
}
@Override
public void setDozing(boolean dozing) {
- mCurrentState.mDozing = dozing;
+ mCurrentState.dozing = dozing;
apply(mCurrentState);
}
@Override
public void setForcePluginOpen(boolean forceOpen, Object token) {
if (forceOpen) {
- mCurrentState.mForceOpenTokens.add(token);
+ mCurrentState.forceOpenTokens.add(token);
} else {
- mCurrentState.mForceOpenTokens.remove(token);
+ mCurrentState.forceOpenTokens.remove(token);
}
- final boolean previousForceOpenState = mCurrentState.mForcePluginOpen;
- mCurrentState.mForcePluginOpen = !mCurrentState.mForceOpenTokens.isEmpty();
- if (previousForceOpenState != mCurrentState.mForcePluginOpen) {
+ final boolean previousForceOpenState = mCurrentState.forcePluginOpen;
+ mCurrentState.forcePluginOpen = !mCurrentState.forceOpenTokens.isEmpty();
+ if (previousForceOpenState != mCurrentState.forcePluginOpen) {
apply(mCurrentState);
if (mForcePluginOpenListener != null) {
- mForcePluginOpenListener.onChange(mCurrentState.mForcePluginOpen);
+ mForcePluginOpenListener.onChange(mCurrentState.forcePluginOpen);
}
}
}
@@ -760,12 +799,12 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
*/
@Override
public boolean getForcePluginOpen() {
- return mCurrentState.mForcePluginOpen;
+ return mCurrentState.forcePluginOpen;
}
@Override
public void setNotTouchable(boolean notTouchable) {
- mCurrentState.mNotTouchable = notTouchable;
+ mCurrentState.windowNotTouchable = notTouchable;
apply(mCurrentState);
}
@@ -774,7 +813,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
*/
@Override
public boolean getPanelExpanded() {
- return mCurrentState.mPanelExpanded;
+ return mCurrentState.panelExpanded;
}
@Override
@@ -797,11 +836,16 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
if (mNotificationShadeView != null && mNotificationShadeView.getViewRootImpl() != null) {
mNotificationShadeView.getViewRootImpl().dump(" ", pw);
}
+ new DumpsysTableLogger(
+ TAG,
+ NotificationShadeWindowState.TABLE_HEADERS,
+ mStateBuffer.toList()
+ ).printTableData(pw);
}
@Override
public boolean isShowingWallpaper() {
- return !mCurrentState.mBackdropShowing;
+ return !mCurrentState.mediaBackdropShowing;
}
@Override
@@ -831,7 +875,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
*/
@Override
public void setKeyguardGoingAway(boolean goingAway) {
- mCurrentState.mKeyguardGoingAway = goingAway;
+ mCurrentState.keyguardGoingAway = goingAway;
apply(mCurrentState);
}
@@ -843,87 +887,13 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
@Override
public void setRequestTopUi(boolean requestTopUi, String componentTag) {
if (requestTopUi) {
- mCurrentState.mComponentsForcingTopUi.add(componentTag);
+ mCurrentState.componentsForcingTopUi.add(componentTag);
} else {
- mCurrentState.mComponentsForcingTopUi.remove(componentTag);
+ mCurrentState.componentsForcingTopUi.remove(componentTag);
}
apply(mCurrentState);
}
- private static class State {
- boolean mKeyguardShowing;
- boolean mKeyguardOccluded;
- boolean mKeyguardNeedsInput;
- boolean mPanelVisible;
- boolean mPanelExpanded;
- boolean mNotificationShadeFocusable;
- boolean mBouncerShowing;
- boolean mKeyguardFadingAway;
- boolean mKeyguardGoingAway;
- boolean mQsExpanded;
- boolean mHeadsUpShowing;
- boolean mLightRevealScrimOpaque;
- boolean mForceCollapsed;
- boolean mForceDozeBrightness;
- boolean mForceUserActivity;
- boolean mLaunchingActivity;
- boolean mBackdropShowing;
- boolean mWallpaperSupportsAmbientMode;
- boolean mNotTouchable;
- Set<String> mComponentsForcingTopUi = new HashSet<>();
- Set<Object> mForceOpenTokens = new HashSet<>();
-
- /**
- * The status bar state from {@link CentralSurfaces}.
- */
- int mStatusBarState;
-
- boolean mRemoteInputActive;
- boolean mForcePluginOpen;
- boolean mDozing;
- int mScrimsVisibility;
- int mBackgroundBlurRadius;
-
- private boolean isKeyguardShowingAndNotOccluded() {
- return mKeyguardShowing && !mKeyguardOccluded;
- }
-
- @Override
- public String toString() {
- return new StringBuilder()
- .append("State{")
- .append(" mKeyguardShowing=").append(mKeyguardShowing)
- .append(", mKeyguardOccluded=").append(mKeyguardOccluded)
- .append(", mKeyguardNeedsInput=").append(mKeyguardNeedsInput)
- .append(", mPanelVisible=").append(mPanelVisible)
- .append(", mPanelExpanded=").append(mPanelExpanded)
- .append(", mNotificationShadeFocusable=").append(mNotificationShadeFocusable)
- .append(", mBouncerShowing=").append(mBouncerShowing)
- .append(", mKeyguardFadingAway=").append(mKeyguardFadingAway)
- .append(", mKeyguardGoingAway=").append(mKeyguardGoingAway)
- .append(", mQsExpanded=").append(mQsExpanded)
- .append(", mHeadsUpShowing=").append(mHeadsUpShowing)
- .append(", mLightRevealScrimOpaque=").append(mLightRevealScrimOpaque)
- .append(", mForceCollapsed=").append(mForceCollapsed)
- .append(", mForceDozeBrightness=").append(mForceDozeBrightness)
- .append(", mForceUserActivity=").append(mForceUserActivity)
- .append(", mLaunchingActivity=").append(mLaunchingActivity)
- .append(", mBackdropShowing=").append(mBackdropShowing)
- .append(", mWallpaperSupportsAmbientMode=")
- .append(mWallpaperSupportsAmbientMode)
- .append(", mNotTouchable=").append(mNotTouchable)
- .append(", mComponentsForcingTopUi=").append(mComponentsForcingTopUi)
- .append(", mForceOpenTokens=").append(mForceOpenTokens)
- .append(", mStatusBarState=").append(mStatusBarState)
- .append(", mRemoteInputActive=").append(mRemoteInputActive)
- .append(", mForcePluginOpen=").append(mForcePluginOpen)
- .append(", mDozing=").append(mDozing)
- .append(", mScrimsVisibility=").append(mScrimsVisibility)
- .append(", mBackgroundBlurRadius=").append(mBackgroundBlurRadius)
- .append('}').toString();
- }
- }
-
private final StateListener mStateListener = new StateListener() {
@Override
public void onStateChanged(int newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
new file mode 100644
index 000000000000..736404aa548a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import com.android.systemui.dump.DumpsysTableLogger
+import com.android.systemui.dump.Row
+import com.android.systemui.plugins.util.RingBuffer
+import com.android.systemui.shade.NotificationShadeWindowState.Buffer
+import com.android.systemui.statusbar.StatusBarState
+
+/**
+ * Represents state of shade window, used by [NotificationShadeWindowControllerImpl].
+ * Contains nested class [Buffer] for pretty table logging in bug reports.
+ */
+class NotificationShadeWindowState(
+ @JvmField var keyguardShowing: Boolean = false,
+ @JvmField var keyguardOccluded: Boolean = false,
+ @JvmField var keyguardNeedsInput: Boolean = false,
+ @JvmField var panelVisible: Boolean = false,
+ /** shade panel is expanded (expansion fraction > 0) */
+ @JvmField var panelExpanded: Boolean = false,
+ @JvmField var notificationShadeFocusable: Boolean = false,
+ @JvmField var bouncerShowing: Boolean = false,
+ @JvmField var keyguardFadingAway: Boolean = false,
+ @JvmField var keyguardGoingAway: Boolean = false,
+ @JvmField var qsExpanded: Boolean = false,
+ @JvmField var headsUpNotificationShowing: Boolean = false,
+ @JvmField var lightRevealScrimOpaque: Boolean = false,
+ @JvmField var forceWindowCollapsed: Boolean = false,
+ @JvmField var forceDozeBrightness: Boolean = false,
+ // TODO: forceUserActivity seems to be unused, delete?
+ @JvmField var forceUserActivity: Boolean = false,
+ @JvmField var launchingActivityFromNotification: Boolean = false,
+ @JvmField var mediaBackdropShowing: Boolean = false,
+ @JvmField var wallpaperSupportsAmbientMode: Boolean = false,
+ @JvmField var windowNotTouchable: Boolean = false,
+ @JvmField var componentsForcingTopUi: MutableSet<String> = mutableSetOf(),
+ @JvmField var forceOpenTokens: MutableSet<Any> = mutableSetOf(),
+ /** one of [StatusBarState] */
+ @JvmField var statusBarState: Int = 0,
+ @JvmField var remoteInputActive: Boolean = false,
+ @JvmField var forcePluginOpen: Boolean = false,
+ @JvmField var dozing: Boolean = false,
+ @JvmField var scrimsVisibility: Int = 0,
+ @JvmField var backgroundBlurRadius: Int = 0,
+) {
+
+ fun isKeyguardShowingAndNotOccluded(): Boolean {
+ return keyguardShowing && !keyguardOccluded
+ }
+
+ /** List of [String] to be used as a [Row] with [DumpsysTableLogger]. */
+ val asStringList: List<String> by lazy {
+ listOf(
+ keyguardShowing.toString(),
+ keyguardOccluded.toString(),
+ keyguardNeedsInput.toString(),
+ panelVisible.toString(),
+ panelExpanded.toString(),
+ notificationShadeFocusable.toString(),
+ bouncerShowing.toString(),
+ keyguardFadingAway.toString(),
+ keyguardGoingAway.toString(),
+ qsExpanded.toString(),
+ headsUpNotificationShowing.toString(),
+ lightRevealScrimOpaque.toString(),
+ forceWindowCollapsed.toString(),
+ forceDozeBrightness.toString(),
+ forceUserActivity.toString(),
+ launchingActivityFromNotification.toString(),
+ mediaBackdropShowing.toString(),
+ wallpaperSupportsAmbientMode.toString(),
+ windowNotTouchable.toString(),
+ componentsForcingTopUi.toString(),
+ forceOpenTokens.toString(),
+ StatusBarState.toString(statusBarState),
+ remoteInputActive.toString(),
+ forcePluginOpen.toString(),
+ dozing.toString(),
+ scrimsVisibility.toString(),
+ backgroundBlurRadius.toString()
+ )
+ }
+
+ /**
+ * [RingBuffer] to store [NotificationShadeWindowState]. After the buffer is full, it will
+ * recycle old events.
+ */
+ class Buffer(capacity: Int) {
+
+ private val buffer = RingBuffer(capacity) { NotificationShadeWindowState() }
+
+ /** Insert a new element in the buffer. */
+ fun insert(
+ keyguardShowing: Boolean,
+ keyguardOccluded: Boolean,
+ keyguardNeedsInput: Boolean,
+ panelVisible: Boolean,
+ panelExpanded: Boolean,
+ notificationShadeFocusable: Boolean,
+ bouncerShowing: Boolean,
+ keyguardFadingAway: Boolean,
+ keyguardGoingAway: Boolean,
+ qsExpanded: Boolean,
+ headsUpShowing: Boolean,
+ lightRevealScrimOpaque: Boolean,
+ forceCollapsed: Boolean,
+ forceDozeBrightness: Boolean,
+ forceUserActivity: Boolean,
+ launchingActivity: Boolean,
+ backdropShowing: Boolean,
+ wallpaperSupportsAmbientMode: Boolean,
+ notTouchable: Boolean,
+ componentsForcingTopUi: MutableSet<String>,
+ forceOpenTokens: MutableSet<Any>,
+ statusBarState: Int,
+ remoteInputActive: Boolean,
+ forcePluginOpen: Boolean,
+ dozing: Boolean,
+ scrimsVisibility: Int,
+ backgroundBlurRadius: Int,
+ ) {
+ buffer.advance().apply {
+ this.keyguardShowing = keyguardShowing
+ this.keyguardOccluded = keyguardOccluded
+ this.keyguardNeedsInput = keyguardNeedsInput
+ this.panelVisible = panelVisible
+ this.panelExpanded = panelExpanded
+ this.notificationShadeFocusable = notificationShadeFocusable
+ this.bouncerShowing = bouncerShowing
+ this.keyguardFadingAway = keyguardFadingAway
+ this.keyguardGoingAway = keyguardGoingAway
+ this.qsExpanded = qsExpanded
+ this.headsUpNotificationShowing = headsUpShowing
+ this.lightRevealScrimOpaque = lightRevealScrimOpaque
+ this.forceWindowCollapsed = forceCollapsed
+ this.forceDozeBrightness = forceDozeBrightness
+ this.forceUserActivity = forceUserActivity
+ this.launchingActivityFromNotification = launchingActivity
+ this.mediaBackdropShowing = backdropShowing
+ this.wallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode
+ this.windowNotTouchable = notTouchable
+ this.componentsForcingTopUi.clear()
+ this.componentsForcingTopUi.addAll(componentsForcingTopUi)
+ this.forceOpenTokens.clear()
+ this.forceOpenTokens.addAll(forceOpenTokens)
+ this.statusBarState = statusBarState
+ this.remoteInputActive = remoteInputActive
+ this.forcePluginOpen = forcePluginOpen
+ this.dozing = dozing
+ this.scrimsVisibility = scrimsVisibility
+ this.backgroundBlurRadius = backgroundBlurRadius
+ }
+ }
+
+ /**
+ * Returns the content of the buffer (sorted from latest to newest).
+ *
+ * @see [NotificationShadeWindowState.asStringList]
+ */
+ fun toList(): List<Row> {
+ return buffer.asSequence().map { it.asStringList }.toList()
+ }
+ }
+
+ companion object {
+ /** Headers for dumping a table using [DumpsysTableLogger]. */
+ @JvmField
+ val TABLE_HEADERS =
+ listOf(
+ "keyguardShowing",
+ "keyguardOccluded",
+ "keyguardNeedsInput",
+ "panelVisible",
+ "panelExpanded",
+ "notificationShadeFocusable",
+ "bouncerShowing",
+ "keyguardFadingAway",
+ "keyguardGoingAway",
+ "qsExpanded",
+ "headsUpShowing",
+ "lightRevealScrimOpaque",
+ "forceCollapsed",
+ "forceDozeBrightness",
+ "forceUserActivity",
+ "launchingActivity",
+ "backdropShowing",
+ "wallpaperSupportsAmbientMode",
+ "notTouchable",
+ "componentsForcingTopUi",
+ "forceOpenTokens",
+ "statusBarState",
+ "remoteInputActive",
+ "forcePluginOpen",
+ "dozing",
+ "scrimsVisibility",
+ "backgroundBlurRadius"
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index 6acf417f0ea6..1f0cbf9af51c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -58,6 +58,7 @@ import android.widget.FrameLayout;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
import com.android.systemui.R;
+import com.android.systemui.compose.ComposeFacade;
/**
* Combined keyguard and notification panel view. Also holding backdrop and scrims.
@@ -149,6 +150,18 @@ public class NotificationShadeWindowView extends FrameLayout {
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setWillNotDraw(!DEBUG);
+
+ if (ComposeFacade.INSTANCE.isComposeAvailable()) {
+ ComposeFacade.INSTANCE.composeInitializer().onAttachedToWindow(this);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (ComposeFacade.INSTANCE.isComposeAvailable()) {
+ ComposeFacade.INSTANCE.composeInitializer().onDetachedFromWindow(this);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
new file mode 100644
index 000000000000..ab0d6e3a6382
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.smartspace.config
+
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.plugins.BcSmartspaceConfigPlugin
+
+class BcSmartspaceConfigProvider(private val featureFlags: FeatureFlags) :
+ BcSmartspaceConfigPlugin {
+ override val isDefaultDateWeatherDisabled: Boolean
+ get() = featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedFrameLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedFrameLayout.java
index 662f70ef269e..438b0f625fc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedFrameLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedFrameLayout.java
@@ -36,10 +36,6 @@ public class AlphaOptimizedFrameLayout extends FrameLayout implements Launchable
visibility -> {
super.setVisibility(visibility);
return Unit.INSTANCE;
- },
- visibility -> {
- super.setTransitionVisibility(visibility);
- return Unit.INSTANCE;
});
public AlphaOptimizedFrameLayout(Context context) {
@@ -73,9 +69,4 @@ public class AlphaOptimizedFrameLayout extends FrameLayout implements Launchable
public void setVisibility(int visibility) {
mLaunchableViewDelegate.setVisibility(visibility);
}
-
- @Override
- public void setTransitionVisibility(int visibility) {
- mLaunchableViewDelegate.setTransitionVisibility(visibility);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 584a382184f3..4e9f69ce7d35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -23,8 +23,6 @@ import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static com.android.systemui.statusbar.phone.CentralSurfacesImpl.ONLY_CORE_APPS;
-
import android.annotation.Nullable;
import android.app.ITransientNotificationCallback;
import android.app.StatusBarManager;
@@ -45,14 +43,17 @@ import android.hardware.fingerprint.IUdfpsHbmListener;
import android.inputmethodservice.InputMethodService.BackDispositionMode;
import android.media.INearbyMediaDevicesProvider;
import android.media.MediaRoute2Info;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.RemoteException;
import android.util.Pair;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.InsetsState.InternalInsetsType;
import android.view.InsetsVisibilities;
@@ -168,6 +169,7 @@ public class CommandQueue extends IStatusBar.Stub implements
private static final int MSG_SHOW_REAR_DISPLAY_DIALOG = 69 << MSG_SHIFT;
private static final int MSG_GO_TO_FULLSCREEN_FROM_SPLIT = 70 << MSG_SHIFT;
private static final int MSG_ENTER_STAGE_SPLIT_FROM_RUNNING_APP = 71 << MSG_SHIFT;
+ private static final int MSG_SHOW_MEDIA_OUTPUT_SWITCHER = 72 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -492,6 +494,11 @@ public class CommandQueue extends IStatusBar.Stub implements
* @see IStatusBar#enterStageSplitFromRunningApp
*/
default void enterStageSplitFromRunningApp(boolean leftOrTop) {}
+
+ /**
+ * @see IStatusBar#showMediaOutputSwitcher
+ */
+ default void showMediaOutputSwitcher(String packageName) {}
}
public CommandQueue(Context context) {
@@ -535,8 +542,7 @@ public class CommandQueue extends IStatusBar.Stub implements
final int disabled1 = getDisabled1(DEFAULT_DISPLAY);
final int disabled2 = getDisabled2(DEFAULT_DISPLAY);
return (disabled1 & StatusBarManager.DISABLE_EXPAND) == 0
- && (disabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
- && !ONLY_CORE_APPS;
+ && (disabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0;
}
@Override
@@ -1262,6 +1268,20 @@ public class CommandQueue extends IStatusBar.Stub implements
}
@Override
+ public void showMediaOutputSwitcher(String packageName) {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
+ Slog.e(TAG, "Call only allowed from system server.");
+ return;
+ }
+ synchronized (mLock) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = packageName;
+ mHandler.obtainMessage(MSG_SHOW_MEDIA_OUTPUT_SWITCHER, args).sendToTarget();
+ }
+ }
+
+ @Override
public void requestAddTile(
@NonNull ComponentName componentName,
@NonNull CharSequence appName,
@@ -1777,6 +1797,13 @@ public class CommandQueue extends IStatusBar.Stub implements
mCallbacks.get(i).enterStageSplitFromRunningApp((Boolean) msg.obj);
}
break;
+ case MSG_SHOW_MEDIA_OUTPUT_SWITCHER:
+ args = (SomeArgs) msg.obj;
+ String clientPackageName = (String) args.arg1;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).showMediaOutputSwitcher(clientPackageName);
+ }
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsModule.java
new file mode 100644
index 000000000000..a797d4a3a7b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsModule.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.BroadcastReceiver;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+
+/**
+ * Module for {@link com.android.systemui.KeyboardShortcutsReceiver}.
+ */
+@Module
+public abstract class KeyboardShortcutsModule {
+
+ /**
+ *
+ */
+ @Binds
+ @IntoMap
+ @ClassKey(KeyboardShortcutsReceiver.class)
+ public abstract BroadcastReceiver bindKeyboardShortcutsReceiver(
+ KeyboardShortcutsReceiver broadcastReceiver);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index d6ad7d0fb8cf..284973976003 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -43,6 +43,7 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.BcSmartspaceConfigPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
@@ -60,11 +61,11 @@ import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject
-/**
- * Controller for managing the smartspace view on the lockscreen
- */
+/** Controller for managing the smartspace view on the lockscreen */
@SysUISingleton
-class LockscreenSmartspaceController @Inject constructor(
+class LockscreenSmartspaceController
+@Inject
+constructor(
private val context: Context,
private val featureFlags: FeatureFlags,
private val smartspaceManager: SmartspaceManager,
@@ -81,7 +82,8 @@ class LockscreenSmartspaceController @Inject constructor(
@Main private val uiExecutor: Executor,
@Background private val bgExecutor: Executor,
@Main private val handler: Handler,
- optionalPlugin: Optional<BcSmartspaceDataPlugin>
+ optionalPlugin: Optional<BcSmartspaceDataPlugin>,
+ optionalConfigPlugin: Optional<BcSmartspaceConfigPlugin>,
) {
companion object {
private const val TAG = "LockscreenSmartspaceController"
@@ -89,6 +91,7 @@ class LockscreenSmartspaceController @Inject constructor(
private var session: SmartspaceSession? = null
private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
+ private val configPlugin: BcSmartspaceConfigPlugin? = optionalConfigPlugin.orElse(null)
// Smartspace can be used on multiple displays, such as when the user casts their screen
private var smartspaceViews = mutableSetOf<SmartspaceView>()
@@ -240,6 +243,7 @@ class LockscreenSmartspaceController @Inject constructor(
val ssView = plugin.getView(parent)
ssView.setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
ssView.registerDataProvider(plugin)
+ ssView.registerConfigProvider(configPlugin)
ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
override fun startIntent(view: View, intent: Intent, showOnLockscreen: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
index 3072c810d31b..05a9a427870e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -35,14 +35,6 @@ class NotifPipelineFlags @Inject constructor(
fun fsiOnDNDUpdate(): Boolean = featureFlags.isEnabled(Flags.FSI_ON_DND_UPDATE)
- val isStabilityIndexFixEnabled: Boolean by lazy {
- featureFlags.isEnabled(Flags.STABILITY_INDEX_FIX)
- }
-
- val isSemiStableSortEnabled: Boolean by lazy {
- featureFlags.isEnabled(Flags.SEMI_STABLE_SORT)
- }
-
val shouldFilterUnseenNotifsOnKeyguard: Boolean by lazy {
featureFlags.isEnabled(Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
index 84ab0d1190f0..b5fce4163bb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
@@ -98,13 +98,11 @@ data class ListAttachState private constructor(
* This can happen if the entry is removed from a group that was broken up or if the entry was
* filtered out during any of the filtering steps.
*/
- fun detach(includingStableIndex: Boolean) {
+ fun detach() {
parent = null
section = null
promoter = null
- if (includingStableIndex) {
- stableIndex = -1
- }
+ stableIndex = -1
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 65a21a4e2190..4065b98ab0c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -965,8 +965,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
* filtered out during any of the filtering steps.
*/
private void annulAddition(ListEntry entry) {
- // NOTE(b/241229236): Don't clear stableIndex until we fix stability fragility
- entry.getAttachState().detach(/* includingStableIndex= */ mFlags.isSemiStableSortEnabled());
+ entry.getAttachState().detach();
}
private void assignSections() {
@@ -986,50 +985,10 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
private void sortListAndGroups() {
Trace.beginSection("ShadeListBuilder.sortListAndGroups");
- if (mFlags.isSemiStableSortEnabled()) {
- sortWithSemiStableSort();
- } else {
- sortWithLegacyStability();
- }
+ sortWithSemiStableSort();
Trace.endSection();
}
- private void sortWithLegacyStability() {
- // Sort all groups and the top level list
- for (ListEntry entry : mNotifList) {
- if (entry instanceof GroupEntry) {
- GroupEntry parent = (GroupEntry) entry;
- parent.sortChildren(mGroupChildrenComparator);
- }
- }
- mNotifList.sort(mTopLevelComparator);
- assignIndexes(mNotifList);
-
- // Check for suppressed order changes
- if (!getStabilityManager().isEveryChangeAllowed()) {
- mForceReorderable = true;
- boolean isSorted = isShadeSortedLegacy();
- mForceReorderable = false;
- if (!isSorted) {
- getStabilityManager().onEntryReorderSuppressed();
- }
- }
- }
-
- private boolean isShadeSortedLegacy() {
- if (!isSorted(mNotifList, mTopLevelComparator)) {
- return false;
- }
- for (ListEntry entry : mNotifList) {
- if (entry instanceof GroupEntry) {
- if (!isSorted(((GroupEntry) entry).getChildren(), mGroupChildrenComparator)) {
- return false;
- }
- }
- }
- return true;
- }
-
private void sortWithSemiStableSort() {
// Sort each group's children
boolean allSorted = true;
@@ -1100,29 +1059,16 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
sectionMemberIndex = 0;
currentSection = section;
}
- if (mFlags.isStabilityIndexFixEnabled()) {
- entry.getAttachState().setStableIndex(sectionMemberIndex++);
- if (entry instanceof GroupEntry) {
- final GroupEntry parent = (GroupEntry) entry;
- final NotificationEntry summary = parent.getSummary();
- if (summary != null) {
- summary.getAttachState().setStableIndex(sectionMemberIndex++);
- }
- for (NotificationEntry child : parent.getChildren()) {
- child.getAttachState().setStableIndex(sectionMemberIndex++);
- }
+ entry.getAttachState().setStableIndex(sectionMemberIndex++);
+ if (entry instanceof GroupEntry) {
+ final GroupEntry parent = (GroupEntry) entry;
+ final NotificationEntry summary = parent.getSummary();
+ if (summary != null) {
+ summary.getAttachState().setStableIndex(sectionMemberIndex++);
}
- } else {
- // This old implementation uses the same index number for the group as the first
- // child, and fails to assign an index to the summary. Remove once tested.
- entry.getAttachState().setStableIndex(sectionMemberIndex);
- if (entry instanceof GroupEntry) {
- final GroupEntry parent = (GroupEntry) entry;
- for (NotificationEntry child : parent.getChildren()) {
- child.getAttachState().setStableIndex(sectionMemberIndex++);
- }
+ for (NotificationEntry child : parent.getChildren()) {
+ child.getAttachState().setStableIndex(sectionMemberIndex++);
}
- sectionMemberIndex++;
}
}
}
@@ -1272,11 +1218,6 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
o2.getSectionIndex());
if (cmp != 0) return cmp;
- cmp = mFlags.isSemiStableSortEnabled() ? 0 : Integer.compare(
- getStableOrderIndex(o1),
- getStableOrderIndex(o2));
- if (cmp != 0) return cmp;
-
NotifComparator sectionComparator = getSectionComparator(o1, o2);
if (sectionComparator != null) {
cmp = sectionComparator.compare(o1, o2);
@@ -1301,12 +1242,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
private final Comparator<NotificationEntry> mGroupChildrenComparator = (o1, o2) -> {
- int cmp = mFlags.isSemiStableSortEnabled() ? 0 : Integer.compare(
- getStableOrderIndex(o1),
- getStableOrderIndex(o2));
- if (cmp != 0) return cmp;
-
- cmp = Integer.compare(
+ int cmp = Integer.compare(
o1.getRepresentativeEntry().getRanking().getRank(),
o2.getRepresentativeEntry().getRanking().getRank());
if (cmp != 0) return cmp;
@@ -1317,25 +1253,6 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
return cmp;
};
- /**
- * A flag that is set to true when we want to run the comparators as if all reordering is
- * allowed. This is used to check if the list is "out of order" after the sort is complete.
- */
- private boolean mForceReorderable = false;
-
- private int getStableOrderIndex(ListEntry entry) {
- if (mForceReorderable) {
- // this is used to determine if the list is correctly sorted
- return -1;
- }
- if (getStabilityManager().isEntryReorderingAllowed(entry)) {
- // let the stability manager constrain or allow reordering
- return -1;
- }
- // NOTE(b/241229236): Can't use cleared section index until we fix stability fragility
- return entry.getPreviousAttachState().getStableIndex();
- }
-
@Nullable
private Integer getStableOrderRank(ListEntry entry) {
if (getStabilityManager().isEntryReorderingAllowed(entry)) {
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 9f50aef6de11..9275e2b603c3 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
@@ -79,6 +79,8 @@ import com.android.internal.widget.CallLayout;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -177,6 +179,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
private Optional<BubblesManager> mBubblesManagerOptional;
private MetricsLogger mMetricsLogger;
+ private FeatureFlags mFeatureFlags;
private int mIconTransformContentShift;
private int mMaxHeadsUpHeightBeforeN;
private int mMaxHeadsUpHeightBeforeP;
@@ -277,7 +280,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private boolean mChildIsExpanding;
private boolean mJustClicked;
- private boolean mIconAnimationRunning;
+ private boolean mAnimationRunning;
private boolean mShowNoBackground;
private ExpandableNotificationRow mNotificationParent;
private OnExpandClickListener mOnExpandClickListener;
@@ -451,10 +454,26 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mPublicLayout;
}
- public void setIconAnimationRunning(boolean running) {
- for (NotificationContentView l : mLayouts) {
- setIconAnimationRunning(running, l);
+ /**
+ * Sets animations running in the layouts of this row, including public, private, and children.
+ * @param running whether the animations should be started running or stopped.
+ */
+ public void setAnimationRunning(boolean running) {
+ // Sets animations running in the private/public layouts.
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ANIMATE_BIG_PICTURE)) {
+ for (NotificationContentView l : mLayouts) {
+ if (l != null) {
+ l.setContentAnimationRunning(running);
+ setIconAnimationRunning(running, l);
+ }
+ }
+ } else {
+ for (NotificationContentView l : mLayouts) {
+ setIconAnimationRunning(running, l);
+ }
}
+ // For groups summaries with children, we want to set the children containers
+ // animating as well.
if (mIsSummaryWithChildren) {
NotificationViewWrapper viewWrapper = mChildrenContainer.getNotificationViewWrapper();
if (viewWrapper != null) {
@@ -468,12 +487,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mChildrenContainer.getAttachedChildren();
for (int i = 0; i < notificationChildren.size(); i++) {
ExpandableNotificationRow child = notificationChildren.get(i);
- child.setIconAnimationRunning(running);
+ child.setAnimationRunning(running);
}
}
- mIconAnimationRunning = running;
+ mAnimationRunning = running;
}
+ /**
+ * Starts or stops animations of the icons in all potential content views (regardless of
+ * whether they're contracted, expanded, etc).
+ *
+ * @param running whether to start or stop the icon's animation.
+ */
private void setIconAnimationRunning(boolean running, NotificationContentView layout) {
if (layout != null) {
View contractedChild = layout.getContractedChild();
@@ -485,16 +510,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
+ /**
+ * Starts or stops animations of the icon in the provided view's icon and right icon.
+ *
+ * @param running whether to start or stop the icon's animation.
+ * @param child the view with the icon to start or stop.
+ */
private void setIconAnimationRunningForChild(boolean running, View child) {
if (child != null) {
ImageView icon = child.findViewById(com.android.internal.R.id.icon);
- setIconRunning(icon, running);
+ setImageViewAnimationRunning(icon, running);
ImageView rightIcon = child.findViewById(com.android.internal.R.id.right_icon);
- setIconRunning(rightIcon, running);
+ setImageViewAnimationRunning(rightIcon, running);
}
}
- private void setIconRunning(ImageView imageView, boolean running) {
+ /**
+ * Starts or stops the animation of a provided image view if it's an AnimationDrawable or an
+ * AnimatedVectorDrawable.
+ *
+ * @param imageView the image view on which to start/stop animation.
+ * @param running whether to start or stop the view's animation.
+ */
+ private void setImageViewAnimationRunning(ImageView imageView, boolean running) {
if (imageView != null) {
Drawable drawable = imageView.getDrawable();
if (drawable instanceof AnimationDrawable) {
@@ -561,8 +599,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mChildrenContainer.recreateNotificationHeader(mExpandClickListener, isConversation());
mChildrenContainer.onNotificationUpdated();
}
- if (mIconAnimationRunning) {
- setIconAnimationRunning(true);
+ if (mAnimationRunning) {
+ setAnimationRunning(true);
}
if (mLastChronometerRunning) {
setChronometerRunning(true);
@@ -1038,7 +1076,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
notifyHeightChanged(false /* needsAnimation */);
}
if (pinned) {
- setIconAnimationRunning(true);
+ setAnimationRunning(true);
mExpandedWhenPinned = false;
} else if (mExpandedWhenPinned) {
setUserExpanded(true);
@@ -1627,6 +1665,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
);
}
+ /**
+ * Constructs an ExpandableNotificationRow.
+ * @param context context passed to image resolver
+ * @param attrs attributes used to initialize parent view
+ */
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
mImageResolver = new NotificationInlineImageResolver(context,
@@ -1662,7 +1705,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
NotificationGutsManager gutsManager,
MetricsLogger metricsLogger,
SmartReplyConstants smartReplyConstants,
- SmartReplyController smartReplyController) {
+ SmartReplyController smartReplyController,
+ FeatureFlags featureFlags) {
mEntry = entry;
mAppName = appName;
if (mMenuRow == null) {
@@ -1697,6 +1741,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mBubblesManagerOptional = bubblesManagerOptional;
mNotificationGutsManager = gutsManager;
mMetricsLogger = metricsLogger;
+ mFeatureFlags = featureFlags;
}
private void initDimens() {
@@ -3588,11 +3633,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@VisibleForTesting
protected void setPrivateLayout(NotificationContentView privateLayout) {
mPrivateLayout = privateLayout;
+ mLayouts = new NotificationContentView[]{mPrivateLayout, mPublicLayout};
}
@VisibleForTesting
protected void setPublicLayout(NotificationContentView publicLayout) {
mPublicLayout = publicLayout;
+ mLayouts = new NotificationContentView[]{mPrivateLayout, mPublicLayout};
}
/**
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 d1138608805b..bb92dfcdfcb5 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
@@ -219,7 +219,8 @@ public class ExpandableNotificationRowController implements NotifViewController
mNotificationGutsManager,
mMetricsLogger,
mSmartReplyConstants,
- mSmartReplyController
+ mSmartReplyController,
+ mFeatureFlags
);
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
if (mAllowLongPress) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index e46bf522acff..4a023c41388e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -184,6 +184,8 @@ public class NotificationContentView extends FrameLayout implements Notification
private boolean mRemoteInputVisible;
private int mUnrestrictedContentHeight;
+ private boolean mContentAnimating;
+
public NotificationContentView(Context context, AttributeSet attrs) {
super(context, attrs);
mHybridGroupManager = new HybridGroupManager(getContext());
@@ -2129,8 +2131,49 @@ public class NotificationContentView extends FrameLayout implements Notification
return false;
}
+ /**
+ * Starts and stops animations in the underlying views.
+ * Avoids restarting the animations by checking whether they're already running first.
+ * Return value is used for testing.
+ *
+ * @param running whether to start animations running, or stop them.
+ * @return true if the state of animations changed.
+ */
+ public boolean setContentAnimationRunning(boolean running) {
+ boolean stateChangeRequired = (running != mContentAnimating);
+ if (stateChangeRequired) {
+ // Starts or stops the animations in the potential views.
+ if (mContractedWrapper != null) {
+ mContractedWrapper.setAnimationsRunning(running);
+ }
+ if (mExpandedWrapper != null) {
+ mExpandedWrapper.setAnimationsRunning(running);
+ }
+ if (mHeadsUpWrapper != null) {
+ mHeadsUpWrapper.setAnimationsRunning(running);
+ }
+ // Updates the state tracker.
+ mContentAnimating = running;
+ return true;
+ }
+ return false;
+ }
+
private static class RemoteInputViewData {
@Nullable RemoteInputView mView;
@Nullable RemoteInputViewController mController;
}
+
+ @VisibleForTesting
+ protected void setContractedWrapper(NotificationViewWrapper contractedWrapper) {
+ mContractedWrapper = contractedWrapper;
+ }
+ @VisibleForTesting
+ protected void setExpandedWrapper(NotificationViewWrapper expandedWrapper) {
+ mExpandedWrapper = expandedWrapper;
+ }
+ @VisibleForTesting
+ protected void setHeadsUpWrapper(NotificationViewWrapper headsUpWrapper) {
+ mHeadsUpWrapper = headsUpWrapper;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index 8732696dc7a1..175ba15eebae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -18,11 +18,15 @@ package com.android.systemui.statusbar.notification.row.wrapper;
import android.app.Notification;
import android.content.Context;
+import android.graphics.drawable.AnimatedImageDrawable;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.view.View;
+import com.android.internal.R;
+import com.android.internal.widget.BigPictureNotificationImageView;
import com.android.systemui.statusbar.notification.ImageTransformState;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -31,6 +35,8 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
*/
public class NotificationBigPictureTemplateViewWrapper extends NotificationTemplateViewWrapper {
+ private BigPictureNotificationImageView mImageView;
+
protected NotificationBigPictureTemplateViewWrapper(Context ctx, View view,
ExpandableNotificationRow row) {
super(ctx, view, row);
@@ -39,9 +45,14 @@ public class NotificationBigPictureTemplateViewWrapper extends NotificationTempl
@Override
public void onContentUpdated(ExpandableNotificationRow row) {
super.onContentUpdated(row);
+ resolveViews();
updateImageTag(row.getEntry().getSbn());
}
+ private void resolveViews() {
+ mImageView = mView.findViewById(R.id.big_picture);
+ }
+
private void updateImageTag(StatusBarNotification sbn) {
final Bundle extras = sbn.getNotification().extras;
Icon bigLargeIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG, Icon.class);
@@ -54,4 +65,25 @@ public class NotificationBigPictureTemplateViewWrapper extends NotificationTempl
mRightIcon.setTag(ImageTransformState.ICON_TAG, getLargeIcon(sbn.getNotification()));
}
}
+
+ /**
+ * Starts or stops the animations in any drawables contained in this BigPicture Notification.
+ *
+ * @param running Whether the animations should be set to run.
+ */
+ @Override
+ public void setAnimationsRunning(boolean running) {
+ if (mImageView == null) {
+ return;
+ }
+ Drawable d = mImageView.getDrawable();
+ if (d instanceof AnimatedImageDrawable) {
+ AnimatedImageDrawable animatedImageDrawable = (AnimatedImageDrawable) d;
+ if (running) {
+ animatedImageDrawable.start();
+ } else {
+ animatedImageDrawable.stop();
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index e136055b80b3..10753f215103 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -17,16 +17,20 @@
package com.android.systemui.statusbar.notification.row.wrapper
import android.content.Context
+import android.graphics.drawable.AnimatedImageDrawable
import android.view.View
import android.view.ViewGroup
import com.android.internal.widget.CachingIconView
import com.android.internal.widget.ConversationLayout
+import com.android.internal.widget.MessagingGroup
+import com.android.internal.widget.MessagingImageMessage
import com.android.internal.widget.MessagingLinearLayout
import com.android.systemui.R
import com.android.systemui.statusbar.notification.NotificationFadeAware
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.wrapper.NotificationMessagingTemplateViewWrapper.setCustomImageMessageTransform
+import com.android.systemui.util.children
/**
* Wraps a notification containing a conversation template
@@ -49,6 +53,7 @@ class NotificationConversationTemplateViewWrapper constructor(
private lateinit var expandBtn: View
private lateinit var expandBtnContainer: View
private lateinit var imageMessageContainer: ViewGroup
+ private lateinit var messageContainers: ArrayList<MessagingGroup>
private lateinit var messagingLinearLayout: MessagingLinearLayout
private lateinit var conversationTitleView: View
private lateinit var importanceRing: View
@@ -60,6 +65,7 @@ class NotificationConversationTemplateViewWrapper constructor(
private fun resolveViews() {
messagingLinearLayout = conversationLayout.messagingLinearLayout
imageMessageContainer = conversationLayout.imageMessageContainer
+ messageContainers = conversationLayout.messagingGroups
with(conversationLayout) {
conversationIconContainer =
requireViewById(com.android.internal.R.id.conversation_icon_container)
@@ -146,4 +152,26 @@ class NotificationConversationTemplateViewWrapper constructor(
NotificationFadeAware.setLayerTypeForFaded(expandBtn, faded)
NotificationFadeAware.setLayerTypeForFaded(conversationIconContainer, faded)
}
+
+ // Starts or stops the animations in any drawables contained in this Conversation Notification.
+ override fun setAnimationsRunning(running: Boolean) {
+ // We apply to both the child message containers in a conversation group,
+ // and the top level image message container.
+ val containers = messageContainers.asSequence().map { it.messageContainer } +
+ sequenceOf(imageMessageContainer)
+ val drawables =
+ containers
+ .flatMap { it.children }
+ .mapNotNull { child ->
+ (child as? MessagingImageMessage)?.let { imageMessage ->
+ imageMessage.drawable as? AnimatedImageDrawable
+ }
+ }
+ drawables.toSet().forEach {
+ when {
+ running -> it.start()
+ !running -> it.stop()
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java
index c587ce05b13f..4592fde69a93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java
@@ -17,9 +17,13 @@
package com.android.systemui.statusbar.notification.row.wrapper;
import android.content.Context;
+import android.graphics.drawable.AnimatedImageDrawable;
+import android.graphics.drawable.Drawable;
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.widget.MessagingGroup;
+import com.android.internal.widget.MessagingImageMessage;
import com.android.internal.widget.MessagingLayout;
import com.android.internal.widget.MessagingLinearLayout;
import com.android.systemui.R;
@@ -127,4 +131,40 @@ public class NotificationMessagingTemplateViewWrapper extends NotificationTempla
}
return super.getMinLayoutHeight();
}
+
+ /**
+ * Starts or stops the animations in any drawables contained in this Messaging Notification.
+ *
+ * @param running Whether the animations should be set to run.
+ */
+ @Override
+ public void setAnimationsRunning(boolean running) {
+ if (mMessagingLayout == null) {
+ return;
+ }
+
+ for (MessagingGroup group : mMessagingLayout.getMessagingGroups()) {
+ for (int i = 0; i < group.getMessageContainer().getChildCount(); i++) {
+ View view = group.getMessageContainer().getChildAt(i);
+ // We only need to set animations in MessagingImageMessages.
+ if (!(view instanceof MessagingImageMessage)) {
+ continue;
+ }
+ MessagingImageMessage imageMessage =
+ (com.android.internal.widget.MessagingImageMessage) view;
+
+ // If the drawable isn't an AnimatedImageDrawable, we can't set it to animate.
+ Drawable d = imageMessage.getDrawable();
+ if (!(d instanceof AnimatedImageDrawable)) {
+ continue;
+ }
+ AnimatedImageDrawable animatedImageDrawable = (AnimatedImageDrawable) d;
+ if (running) {
+ animatedImageDrawable.start();
+ } else {
+ animatedImageDrawable.stop();
+ }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 1c22f0933236..ff5b9cbf3c23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -403,4 +403,12 @@ public abstract class NotificationViewWrapper implements TransformableView {
NotificationFadeAware.setLayerTypeForFaded(getIcon(), faded);
NotificationFadeAware.setLayerTypeForFaded(getExpandButton(), faded);
}
+
+ /**
+ * Starts or stops the animations in any drawables contained in this Notification.
+ *
+ * @param running Whether the animations should be set to run.
+ */
+ public void setAnimationsRunning(boolean running) {
+ }
}
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 356ddfa2d482..4837075680ae 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
@@ -3132,7 +3132,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private void updateAnimationState(boolean running, View child) {
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- row.setIconAnimationRunning(running);
+ row.setAnimationRunning(running);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
index 3ccef9d6eb14..eb81c46027e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
@@ -16,25 +16,35 @@
package com.android.systemui.statusbar.phone;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
+
import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.view.IWindowManager;
import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.annotation.NonNull;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.AutoHideUiElement;
+import java.io.PrintWriter;
+
import javax.inject.Inject;
/** A controller to control all auto-hide things. Also see {@link AutoHideUiElement}. */
@SysUISingleton
public class AutoHideController {
private static final String TAG = "AutoHideController";
- private static final long AUTO_HIDE_TIMEOUT_MS = 2250;
+ private static final int AUTO_HIDE_TIMEOUT_MS = 2250;
+ private static final int USER_AUTO_HIDE_TIMEOUT_MS = 350;
+ private final AccessibilityManager mAccessibilityManager;
private final IWindowManager mWindowManagerService;
private final Handler mHandler;
@@ -52,11 +62,12 @@ public class AutoHideController {
};
@Inject
- public AutoHideController(Context context, @Main Handler handler,
+ public AutoHideController(Context context,
+ @Main Handler handler,
IWindowManager iWindowManager) {
+ mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mHandler = handler;
mWindowManagerService = iWindowManager;
-
mDisplayId = context.getDisplayId();
}
@@ -138,7 +149,12 @@ public class AutoHideController {
private void scheduleAutoHide() {
cancelAutoHide();
- mHandler.postDelayed(mAutoHide, AUTO_HIDE_TIMEOUT_MS);
+ mHandler.postDelayed(mAutoHide, getAutoHideTimeout());
+ }
+
+ private int getAutoHideTimeout() {
+ return mAccessibilityManager.getRecommendedTimeoutMillis(AUTO_HIDE_TIMEOUT_MS,
+ FLAG_CONTENT_CONTROLS);
}
public void checkUserAutoHide(MotionEvent event) {
@@ -160,7 +176,13 @@ public class AutoHideController {
private void userAutoHide() {
cancelAutoHide();
- mHandler.postDelayed(mAutoHide, 350); // longer than app gesture -> flag clear
+ // longer than app gesture -> flag clear
+ mHandler.postDelayed(mAutoHide, getUserAutoHideTimeout());
+ }
+
+ private int getUserAutoHideTimeout() {
+ return mAccessibilityManager.getRecommendedTimeoutMillis(USER_AUTO_HIDE_TIMEOUT_MS,
+ FLAG_CONTENT_CONTROLS);
}
private boolean isAnyTransientBarShown() {
@@ -175,6 +197,15 @@ public class AutoHideController {
return false;
}
+ public void dump(@NonNull PrintWriter pw) {
+ pw.println("AutoHideController:");
+ pw.println("\tmAutoHideSuspended=" + mAutoHideSuspended);
+ pw.println("\tisAnyTransientBarShown=" + isAnyTransientBarShown());
+ pw.println("\thasPendingAutoHide=" + mHandler.hasCallbacks(mAutoHide));
+ pw.println("\tgetAutoHideTimeout=" + getAutoHideTimeout());
+ pw.println("\tgetUserAutoHideTimeout=" + getUserAutoHideTimeout());
+ }
+
/**
* Injectable factory for creating a {@link AutoHideController}.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index f1e1f426b467..64c7ff76c9d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -64,7 +64,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
@@ -292,26 +291,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl();
- /**
- * If true, the system is in the half-boot-to-decryption-screen state.
- * Prudently disable QS and notifications.
- */
- public static final boolean ONLY_CORE_APPS;
-
- static {
- boolean onlyCoreApps;
- try {
- IPackageManager packageManager =
- IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
- onlyCoreApps = packageManager != null && packageManager.isOnlyCoreApps();
- } catch (RemoteException e) {
- onlyCoreApps = false;
- }
- ONLY_CORE_APPS = onlyCoreApps;
- }
-
private final Context mContext;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ private final DeviceStateManager mDeviceStateManager;
private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks;
private float mTransitionToFullShadeProgress = 0f;
private NotificationListContainer mNotifListContainer;
@@ -879,8 +861,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mMessageRouter.subscribeTo(MSG_LAUNCH_TRANSITION_TIMEOUT,
id -> onLaunchTransitionTimeout());
- deviceStateManager.registerCallback(mMainExecutor,
- new FoldStateListener(mContext, this::onFoldedStateChanged));
+ mDeviceStateManager = deviceStateManager;
wiredChargingRippleController.registerCallbacks();
mLightRevealScrimViewModelLazy = lightRevealScrimViewModelLazy;
@@ -1069,6 +1050,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
});
+ registerCallbacks();
+
mFalsingManager.addFalsingBeliefListener(mFalsingBeliefListener);
mPluginManager.addPluginListener(
@@ -1124,6 +1107,14 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
@VisibleForTesting
+ /** Registers listeners/callbacks with external dependencies. */
+ void registerCallbacks() {
+ //TODO(b/264502026) move the rest of the listeners here.
+ mDeviceStateManager.registerCallback(mMainExecutor,
+ new FoldStateListener(mContext, this::onFoldedStateChanged));
+ }
+
+ @VisibleForTesting
void initShadeVisibilityListener() {
mShadeController.setVisibilityListener(new ShadeController.ShadeVisibilityListener() {
@Override
@@ -1692,8 +1683,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
|| !mUserSwitcherController.isSimpleUserSwitcher())
&& !isShadeDisabled()
&& ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
- && !mDozing
- && !ONLY_CORE_APPS;
+ && !mDozing;
mNotificationPanelViewController.setQsExpansionEnabledPolicy(expandEnabled);
Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 000fe140882c..d31875935dd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.phone;
import static com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
+import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import android.content.Context;
@@ -44,6 +46,8 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.KeyguardResetCallback;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ListenerSet;
@@ -64,14 +68,6 @@ public class KeyguardBouncer {
private static final String TAG = "PrimaryKeyguardBouncer";
static final long BOUNCER_FACE_DELAY = 1200;
public static final float ALPHA_EXPANSION_THRESHOLD = 0.95f;
- /**
- * Values for the bouncer expansion represented as the panel expansion.
- * Panel expansion 1f = panel fully showing = bouncer fully hidden
- * Panel expansion 0f = panel fully hiding = bouncer fully showing
- */
- public static final float EXPANSION_HIDDEN = 1f;
- public static final float EXPANSION_VISIBLE = 0f;
-
protected final Context mContext;
protected final ViewMediatorCallback mCallback;
protected final ViewGroup mContainer;
@@ -664,56 +660,6 @@ public class KeyguardBouncer {
mExpansionCallbacks.remove(callback);
}
- /**
- * Callback updated when the primary bouncer's show and hide states change.
- */
- public interface PrimaryBouncerExpansionCallback {
- /**
- * Invoked when the bouncer expansion reaches {@link KeyguardBouncer#EXPANSION_VISIBLE}.
- * This is NOT called each time the bouncer is shown, but rather only when the fully
- * shown amount has changed based on the panel expansion. The bouncer's visibility
- * can still change when the expansion amount hasn't changed.
- * See {@link KeyguardBouncer#isShowing()} for the checks for the bouncer showing state.
- */
- default void onFullyShown() {
- }
-
- /**
- * Invoked when the bouncer is starting to transition to a hidden state.
- */
- default void onStartingToHide() {
- }
-
- /**
- * Invoked when the bouncer is starting to transition to a visible state.
- */
- default void onStartingToShow() {
- }
-
- /**
- * Invoked when the bouncer expansion reaches {@link KeyguardBouncer#EXPANSION_HIDDEN}.
- */
- default void onFullyHidden() {
- }
-
- /**
- * From 0f {@link KeyguardBouncer#EXPANSION_VISIBLE} when fully visible
- * to 1f {@link KeyguardBouncer#EXPANSION_HIDDEN} when fully hidden
- */
- default void onExpansionChanged(float bouncerHideAmount) {}
-
- /**
- * Invoked when visibility of KeyguardBouncer has changed.
- * Note the bouncer expansion can be {@link KeyguardBouncer#EXPANSION_VISIBLE}, but the
- * view's visibility can be {@link View.INVISIBLE}.
- */
- default void onVisibilityChanged(boolean isVisible) {}
- }
-
- public interface KeyguardResetCallback {
- void onKeyguardReset();
- }
-
/** Create a {@link KeyguardBouncer} once a container and bouncer callback are available. */
public static class Factory {
private final Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index b965ac97cc1c..ff1b31d8848f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -30,6 +30,9 @@ import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm
+import com.android.systemui.statusbar.policy.DevicePostureController
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN
+import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.tuner.TunerService
import java.io.PrintWriter
@@ -40,11 +43,19 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
private val mKeyguardStateController: KeyguardStateController
private val statusBarStateController: StatusBarStateController
+ private val devicePostureController: DevicePostureController
@BypassOverride private val bypassOverride: Int
private var hasFaceFeature: Boolean
+ @DevicePostureInt private val configFaceAuthSupportedPosture: Int
+ @DevicePostureInt private var postureState: Int = DEVICE_POSTURE_UNKNOWN
private var pendingUnlock: PendingUnlock? = null
private val listeners = mutableListOf<OnBypassStateChangedListener>()
-
+ private val postureCallback = DevicePostureController.Callback { posture ->
+ if (postureState != posture) {
+ postureState = posture
+ notifyListeners()
+ }
+ }
private val faceAuthEnabledChangedCallback = object : KeyguardStateController.Callback {
override fun onFaceAuthEnabledChanged() = notifyListeners()
}
@@ -86,7 +97,8 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
FACE_UNLOCK_BYPASS_NEVER -> false
else -> field
}
- return enabled && mKeyguardStateController.isFaceAuthEnabled
+ return enabled && mKeyguardStateController.isFaceAuthEnabled &&
+ isPostureAllowedForFaceAuth()
}
private set(value) {
field = value
@@ -106,18 +118,31 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
lockscreenUserManager: NotificationLockscreenUserManager,
keyguardStateController: KeyguardStateController,
shadeExpansionStateManager: ShadeExpansionStateManager,
+ devicePostureController: DevicePostureController,
dumpManager: DumpManager
) {
this.mKeyguardStateController = keyguardStateController
this.statusBarStateController = statusBarStateController
+ this.devicePostureController = devicePostureController
bypassOverride = context.resources.getInteger(R.integer.config_face_unlock_bypass_override)
+ configFaceAuthSupportedPosture =
+ context.resources.getInteger(R.integer.config_face_auth_supported_posture)
- hasFaceFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)
+ hasFaceFeature = context.packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)
if (!hasFaceFeature) {
return
}
+ if (configFaceAuthSupportedPosture != DEVICE_POSTURE_UNKNOWN) {
+ devicePostureController.addCallback { posture ->
+ if (postureState != posture) {
+ postureState = posture
+ notifyListeners()
+ }
+ }
+ }
+
dumpManager.registerDumpable("KeyguardBypassController", this)
statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
override fun onStateChanged(newState: Int) {
@@ -203,6 +228,13 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
pendingUnlock = null
}
+ fun isPostureAllowedForFaceAuth(): Boolean {
+ return when (configFaceAuthSupportedPosture) {
+ DEVICE_POSTURE_UNKNOWN -> true
+ else -> (postureState == configFaceAuthSupportedPosture)
+ }
+ }
+
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("KeyguardBypassController:")
if (pendingUnlock != null) {
@@ -219,6 +251,7 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
pw.println(" launchingAffordance: $launchingAffordance")
pw.println(" qSExpanded: $qsExpanded")
pw.println(" hasFaceFeature: $hasFaceFeature")
+ pw.println(" postureState: $postureState")
}
/** Registers a listener for bypass state changes. */
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 ee8b86160f51..f78472386006 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -53,6 +53,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -147,7 +148,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
* 0, the bouncer is visible.
*/
@FloatRange(from = 0, to = 1)
- private float mBouncerHiddenFraction = KeyguardBouncer.EXPANSION_HIDDEN;
+ private float mBouncerHiddenFraction = KeyguardBouncerConstants.EXPANSION_HIDDEN;
/**
* Set whether an unocclusion animation is currently running on the notification panel. Used
@@ -810,7 +811,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
if (mState == ScrimState.DREAMING
- && mBouncerHiddenFraction != KeyguardBouncer.EXPANSION_HIDDEN) {
+ && mBouncerHiddenFraction != KeyguardBouncerConstants.EXPANSION_HIDDEN) {
final float interpolatedFraction =
BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(
mBouncerHiddenFraction);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 1a14a0363763..24ad55d67bb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -79,12 +79,30 @@ public interface StatusBarIconController {
/** Refresh the state of an IconManager by recreating the views */
void refreshIconGroup(IconManager iconManager);
- /** */
+
+ /**
+ * Adds or updates an icon for a given slot for a **tile service icon**.
+ *
+ * TODO(b/265307726): Merge with {@link #setIcon(String, StatusBarIcon)} or make this method
+ * much more clearly distinct from that method.
+ */
void setExternalIcon(String slot);
- /** */
+
+ /**
+ * Adds or updates an icon for the given slot for **internal system icons**.
+ *
+ * TODO(b/265307726): Rename to `setInternalIcon`, or merge this appropriately with the
+ * {@link #setIcon(String, StatusBarIcon)} method.
+ */
void setIcon(String slot, int resourceId, CharSequence contentDescription);
- /** */
+
+ /**
+ * Adds or updates an icon for the given slot for an **externally-provided icon**.
+ *
+ * TODO(b/265307726): Rename to `setExternalIcon` or something similar.
+ */
void setIcon(String slot, StatusBarIcon icon);
+
/** */
void setWifiIcon(String slot, WifiIconState state);
@@ -133,9 +151,17 @@ public interface StatusBarIconController {
* TAG_PRIMARY to refer to the first icon at a given slot.
*/
void removeIcon(String slot, int tag);
+
/** */
void removeAllIconsForSlot(String slot);
+ /**
+ * Removes all the icons for the given slot.
+ *
+ * Only use this for icons that have come from **an external process**.
+ */
+ void removeAllIconsForExternalSlot(String slot);
+
// TODO: See if we can rename this tunable name.
String ICON_HIDE_LIST = "icon_blacklist";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 9fbe6cbc0e32..416bc7141eeb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -28,6 +28,8 @@ import android.util.ArraySet;
import android.util.Log;
import android.view.ViewGroup;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -63,6 +65,10 @@ public class StatusBarIconControllerImpl implements Tunable,
ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController, DemoMode {
private static final String TAG = "StatusBarIconController";
+ // Use this suffix to prevent external icon slot names from unintentionally overriding our
+ // internal, system-level slot names. See b/255428281.
+ @VisibleForTesting
+ protected static final String EXTERNAL_SLOT_SUFFIX = "__external";
private final StatusBarIconList mStatusBarIconList;
private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
@@ -346,21 +352,26 @@ public class StatusBarIconControllerImpl implements Tunable,
@Override
public void setExternalIcon(String slot) {
- int viewIndex = mStatusBarIconList.getViewIndex(slot, 0);
+ String slotName = createExternalSlotName(slot);
+ int viewIndex = mStatusBarIconList.getViewIndex(slotName, 0);
int height = mContext.getResources().getDimensionPixelSize(
R.dimen.status_bar_icon_drawing_size);
mIconGroups.forEach(l -> l.onIconExternal(viewIndex, height));
}
- //TODO: remove this (used in command queue and for 3rd party tiles?)
+ // Override for *both* CommandQueue.Callbacks AND StatusBarIconController.
+ // TODO(b/265307726): Pull out the CommandQueue callbacks into a member variable to
+ // differentiate between those callback methods and StatusBarIconController methods.
+ @Override
public void setIcon(String slot, StatusBarIcon icon) {
+ String slotName = createExternalSlotName(slot);
if (icon == null) {
- removeAllIconsForSlot(slot);
+ removeAllIconsForSlot(slotName);
return;
}
StatusBarIconHolder holder = StatusBarIconHolder.fromIcon(icon);
- setIcon(slot, holder);
+ setIcon(slotName, holder);
}
private void setIcon(String slot, @NonNull StatusBarIconHolder holder) {
@@ -406,10 +417,12 @@ public class StatusBarIconControllerImpl implements Tunable,
}
}
- /** */
+ // CommandQueue.Callbacks override
+ // TODO(b/265307726): Pull out the CommandQueue callbacks into a member variable to
+ // differentiate between those callback methods and StatusBarIconController methods.
@Override
public void removeIcon(String slot) {
- removeAllIconsForSlot(slot);
+ removeAllIconsForExternalSlot(slot);
}
/** */
@@ -423,6 +436,11 @@ public class StatusBarIconControllerImpl implements Tunable,
mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex));
}
+ @Override
+ public void removeAllIconsForExternalSlot(String slotName) {
+ removeAllIconsForSlot(createExternalSlotName(slotName));
+ }
+
/** */
@Override
public void removeAllIconsForSlot(String slotName) {
@@ -506,4 +524,12 @@ public class StatusBarIconControllerImpl implements Tunable,
public void onDensityOrFontScaleChanged() {
refreshIconGroups();
}
+
+ private String createExternalSlotName(String slot) {
+ if (slot.endsWith(EXTERNAL_SLOT_SUFFIX)) {
+ return slot;
+ } else {
+ return slot + EXTERNAL_SLOT_SUFFIX;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 7d917bd9cd1c..be06b05fc136 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone;
import static android.view.WindowInsets.Type.navigationBars;
+import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
@@ -36,7 +37,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.WindowManagerGlobal;
-import android.window.OnBackInvokedCallback;
+import android.window.BackEvent;
+import android.window.OnBackAnimationCallback;
import android.window.OnBackInvokedDispatcher;
import androidx.annotation.NonNull;
@@ -60,6 +62,7 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.data.BouncerView;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -76,7 +79,6 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.unfold.FoldAodAnimationController;
@@ -184,8 +186,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
isVisible && mDreamOverlayStateController.isOverlayActive());
if (!isVisible) {
- mCentralSurfaces.setPrimaryBouncerHiddenFraction(
- KeyguardBouncer.EXPANSION_HIDDEN);
+ mCentralSurfaces.setPrimaryBouncerHiddenFraction(EXPANSION_HIDDEN);
}
/* Register predictive back callback when keyguard becomes visible, and unregister
@@ -198,11 +199,38 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
};
- private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
- if (DEBUG) {
- Log.d(TAG, "onBackInvokedCallback() called, invoking onBackPressed()");
+ private final OnBackAnimationCallback mOnBackInvokedCallback = new OnBackAnimationCallback() {
+ @Override
+ public void onBackInvoked() {
+ if (DEBUG) {
+ Log.d(TAG, "onBackInvokedCallback() called, invoking onBackPressed()");
+ }
+ onBackPressed();
+ if (shouldPlayBackAnimation()) {
+ mPrimaryBouncerView.getDelegate().getBackCallback().onBackInvoked();
+ }
+ }
+
+ @Override
+ public void onBackProgressed(BackEvent event) {
+ if (shouldPlayBackAnimation()) {
+ mPrimaryBouncerView.getDelegate().getBackCallback().onBackProgressed(event);
+ }
+ }
+
+ @Override
+ public void onBackCancelled() {
+ if (shouldPlayBackAnimation()) {
+ mPrimaryBouncerView.getDelegate().getBackCallback().onBackCancelled();
+ }
+ }
+
+ @Override
+ public void onBackStarted(BackEvent event) {
+ if (shouldPlayBackAnimation()) {
+ mPrimaryBouncerView.getDelegate().getBackCallback().onBackStarted(event);
+ }
}
- onBackPressed();
};
private boolean mIsBackCallbackRegistered = false;
@@ -256,6 +284,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private boolean mIsModernBouncerEnabled;
private boolean mIsUnoccludeTransitionFlagEnabled;
private boolean mIsModernAlternateBouncerEnabled;
+ private boolean mIsBackAnimationEnabled;
private OnDismissAction mAfterKeyguardGoneAction;
private Runnable mKeyguardGoneCancelAction;
@@ -337,6 +366,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mIsUnoccludeTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION);
mIsModernAlternateBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_ALTERNATE_BOUNCER);
mAlternateBouncerInteractor = alternateBouncerInteractor;
+ mIsBackAnimationEnabled =
+ featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM);
}
@Override
@@ -472,6 +503,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
}
+ private boolean shouldPlayBackAnimation() {
+ // Suppress back animation when bouncer shouldn't be dismissed on back invocation.
+ return !needsFullscreenBouncer() && mIsBackAnimationEnabled;
+ }
+
@Override
public void onDensityOrFontScaleChanged() {
hideBouncer(true /* destroyView */);
@@ -485,7 +521,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|| mNotificationPanelViewController.isExpanding());
final boolean isUserTrackingStarted =
- event.getFraction() != KeyguardBouncer.EXPANSION_HIDDEN && event.getTracking();
+ event.getFraction() != EXPANSION_HIDDEN && event.getTracking();
return mKeyguardStateController.isShowing()
&& !primaryBouncerIsOrWillBeShowing()
@@ -535,9 +571,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
} else {
if (mPrimaryBouncer != null) {
- mPrimaryBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ mPrimaryBouncer.setExpansion(EXPANSION_HIDDEN);
} else {
- mPrimaryBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
index 6cd8c78dd52f..9e6bb20f429d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
@@ -16,7 +16,9 @@
package com.android.systemui.statusbar.phone
+import android.view.InsetsFlags
import android.view.InsetsVisibilities
+import android.view.ViewDebug
import android.view.WindowInsetsController.Appearance
import android.view.WindowInsetsController.Behavior
import com.android.internal.statusbar.LetterboxDetails
@@ -148,4 +150,20 @@ private data class SystemBarAttributesParams(
) {
val letterboxesArray = letterboxes.toTypedArray()
val appearanceRegionsArray = appearanceRegions.toTypedArray()
+ override fun toString(): String {
+ val appearanceToString =
+ ViewDebug.flagsToString(InsetsFlags::class.java, "appearance", appearance)
+ return """SystemBarAttributesParams(
+ displayId=$displayId,
+ appearance=$appearanceToString,
+ appearanceRegions=$appearanceRegions,
+ navbarColorManagedByIme=$navbarColorManagedByIme,
+ behavior=$behavior,
+ requestedVisibilities=$requestedVisibilities,
+ packageName='$packageName',
+ letterboxes=$letterboxes,
+ letterboxesArray=${letterboxesArray.contentToString()},
+ appearanceRegionsArray=${appearanceRegionsArray.contentToString()}
+ )""".trimMargin()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ResolvedNetworkType.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ResolvedNetworkType.kt
index 59603874efde..5562e73f0478 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ResolvedNetworkType.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ResolvedNetworkType.kt
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.pipeline.mobile.data.model
import android.telephony.Annotation.NetworkType
+import com.android.settingslib.SignalIcon
+import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
/**
@@ -38,4 +40,12 @@ sealed interface ResolvedNetworkType {
data class OverrideNetworkType(
override val lookupKey: String,
) : ResolvedNetworkType
+
+ /** Represents the carrier merged network. See [CarrierMergedConnectionRepository]. */
+ object CarrierMergedNetworkType : ResolvedNetworkType {
+ // Effectively unused since [iconGroupOverride] is used instead.
+ override val lookupKey: String = "cwf"
+
+ val iconGroupOverride: SignalIcon.MobileIconGroup = TelephonyIcons.CARRIER_MERGED_WIFI
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
index d04996b4d6ce..6187f64e011d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
@@ -22,7 +22,6 @@ import android.telephony.TelephonyManager
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
/**
@@ -50,7 +49,7 @@ interface MobileConnectionRepository {
* A flow that aggregates all necessary callbacks from [TelephonyCallback] into a single
* listener + model.
*/
- val connectionInfo: Flow<MobileConnectionModel>
+ val connectionInfo: StateFlow<MobileConnectionModel>
/** The total number of levels. Used with [SignalDrawable]. */
val numberOfLevels: StateFlow<Int>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
index 97b4c2cadbe5..e0d156aa25f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository
import android.provider.Settings
import android.telephony.CarrierConfigManager
+import android.telephony.SubscriptionManager
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.MobileMappings.Config
@@ -37,6 +38,15 @@ interface MobileConnectionsRepository {
/** Observable for the subscriptionId of the current mobile data connection */
val activeMobileDataSubscriptionId: StateFlow<Int>
+ /**
+ * Observable event for when the active data sim switches but the group stays the same. E.g.,
+ * CBRS switching would trigger this
+ */
+ val activeSubChangedInGroupEvent: Flow<Unit>
+
+ /** Tracks [SubscriptionManager.getDefaultDataSubscriptionId] */
+ val defaultDataSubId: StateFlow<Int>
+
/** The current connectivity status for the default mobile network connection */
val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index 0c8593d60cf5..b93985604fb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -124,6 +124,9 @@ constructor(
realRepository.activeMobileDataSubscriptionId.value
)
+ override val activeSubChangedInGroupEvent: Flow<Unit> =
+ activeRepo.flatMapLatest { it.activeSubChangedInGroupEvent }
+
override val defaultDataSubRatConfig: StateFlow<MobileMappings.Config> =
activeRepo
.flatMapLatest { it.defaultDataSubRatConfig }
@@ -139,6 +142,11 @@ constructor(
override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
activeRepo.flatMapLatest { it.defaultMobileIconGroup }
+ override val defaultDataSubId: StateFlow<Int> =
+ activeRepo
+ .flatMapLatest { it.defaultDataSubId }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), realRepository.defaultDataSubId.value)
+
override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
activeRepo
.flatMapLatest { it.defaultMobileNetworkConnectivity }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index 8ac12379e59e..108834521ebf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -39,11 +39,16 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConn
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel.Mobile
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel.MobileDisabled
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.CarrierMergedConnectionRepository.Companion.createCarrierMergedConnectionModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.MOBILE_CONNECTION_BUFFER_SIZE
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -60,15 +65,19 @@ import kotlinx.coroutines.launch
class DemoMobileConnectionsRepository
@Inject
constructor(
- private val dataSource: DemoModeMobileConnectionDataSource,
+ private val mobileDataSource: DemoModeMobileConnectionDataSource,
+ private val wifiDataSource: DemoModeWifiDataSource,
@Application private val scope: CoroutineScope,
context: Context,
private val logFactory: TableLogBufferFactory,
) : MobileConnectionsRepository {
- private var demoCommandJob: Job? = null
+ private var mobileDemoCommandJob: Job? = null
+ private var wifiDemoCommandJob: Job? = null
- private var connectionRepoCache = mutableMapOf<Int, DemoMobileConnectionRepository>()
+ private var carrierMergedSubId: Int? = null
+
+ private var connectionRepoCache = mutableMapOf<Int, CacheContainer>()
private val subscriptionInfoCache = mutableMapOf<Int, SubscriptionModel>()
val demoModeFinishedEvent = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
@@ -112,6 +121,9 @@ constructor(
subscriptions.value.firstOrNull()?.subscriptionId ?: INVALID_SUBSCRIPTION_ID
)
+ // TODO(b/261029387): consider adding a demo command for this
+ override val activeSubChangedInGroupEvent: Flow<Unit> = flowOf()
+
/** Demo mode doesn't currently support modifications to the mobile mappings */
override val defaultDataSubRatConfig =
MutableStateFlow(MobileMappings.Config.readConfig(context))
@@ -140,56 +152,94 @@ constructor(
private fun <K, V> Map<K, V>.reverse() = entries.associateBy({ it.value }) { it.key }
+ // TODO(b/261029387): add a command for this value
+ override val defaultDataSubId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
+
// TODO(b/261029387): not yet supported
- override val defaultMobileNetworkConnectivity = MutableStateFlow(MobileConnectivityModel())
+ override val defaultMobileNetworkConnectivity =
+ MutableStateFlow(MobileConnectivityModel(isConnected = true, isValidated = true))
override fun getRepoForSubId(subId: Int): DemoMobileConnectionRepository {
- return connectionRepoCache[subId]
- ?: createDemoMobileConnectionRepo(subId).also { connectionRepoCache[subId] = it }
+ val current = connectionRepoCache[subId]?.repo
+ if (current != null) {
+ return current
+ }
+
+ val new = createDemoMobileConnectionRepo(subId)
+ connectionRepoCache[subId] = new
+ return new.repo
}
- private fun createDemoMobileConnectionRepo(subId: Int): DemoMobileConnectionRepository {
- val tableLogBuffer = logFactory.getOrCreate("DemoMobileConnectionLog [$subId]", 100)
+ private fun createDemoMobileConnectionRepo(subId: Int): CacheContainer {
+ val tableLogBuffer =
+ logFactory.getOrCreate(
+ "DemoMobileConnectionLog [$subId]",
+ MOBILE_CONNECTION_BUFFER_SIZE,
+ )
- return DemoMobileConnectionRepository(
- subId,
- tableLogBuffer,
- )
+ val repo =
+ DemoMobileConnectionRepository(
+ subId,
+ tableLogBuffer,
+ )
+ return CacheContainer(repo, lastMobileState = null)
}
override val globalMobileDataSettingChangedEvent = MutableStateFlow(Unit)
fun startProcessingCommands() {
- demoCommandJob =
+ mobileDemoCommandJob =
scope.launch {
- dataSource.mobileEvents.filterNotNull().collect { event -> processEvent(event) }
+ mobileDataSource.mobileEvents.filterNotNull().collect { event ->
+ processMobileEvent(event)
+ }
+ }
+ wifiDemoCommandJob =
+ scope.launch {
+ wifiDataSource.wifiEvents.filterNotNull().collect { event ->
+ processWifiEvent(event)
+ }
}
}
fun stopProcessingCommands() {
- demoCommandJob?.cancel()
+ mobileDemoCommandJob?.cancel()
+ wifiDemoCommandJob?.cancel()
_subscriptions.value = listOf()
connectionRepoCache.clear()
subscriptionInfoCache.clear()
}
- private fun processEvent(event: FakeNetworkEventModel) {
+ private fun processMobileEvent(event: FakeNetworkEventModel) {
when (event) {
is Mobile -> {
processEnabledMobileState(event)
}
is MobileDisabled -> {
- processDisabledMobileState(event)
+ maybeRemoveSubscription(event.subId)
}
}
}
+ private fun processWifiEvent(event: FakeWifiEventModel) {
+ when (event) {
+ is FakeWifiEventModel.WifiDisabled -> disableCarrierMerged()
+ is FakeWifiEventModel.Wifi -> disableCarrierMerged()
+ is FakeWifiEventModel.CarrierMerged -> processCarrierMergedWifiState(event)
+ }
+ }
+
private fun processEnabledMobileState(state: Mobile) {
// get or create the connection repo, and set its values
val subId = state.subId ?: DEFAULT_SUB_ID
maybeCreateSubscription(subId)
val connection = getRepoForSubId(subId)
+ connectionRepoCache[subId]?.lastMobileState = state
+
+ // TODO(b/261029387): until we have a command, use the most recent subId
+ defaultDataSubId.value = subId
+
// This is always true here, because we split out disabled states at the data-source level
connection.dataEnabled.value = true
connection.networkName.value = NetworkNameModel.Derived(state.name)
@@ -198,14 +248,36 @@ constructor(
connection.connectionInfo.value = state.toMobileConnectionModel()
}
- private fun processDisabledMobileState(state: MobileDisabled) {
+ private fun processCarrierMergedWifiState(event: FakeWifiEventModel.CarrierMerged) {
+ // The new carrier merged connection is for a different sub ID, so disable carrier merged
+ // for the current (now old) sub
+ if (carrierMergedSubId != event.subscriptionId) {
+ disableCarrierMerged()
+ }
+
+ // get or create the connection repo, and set its values
+ val subId = event.subscriptionId
+ maybeCreateSubscription(subId)
+ carrierMergedSubId = subId
+
+ val connection = getRepoForSubId(subId)
+ // This is always true here, because we split out disabled states at the data-source level
+ connection.dataEnabled.value = true
+ connection.networkName.value = NetworkNameModel.Derived(CARRIER_MERGED_NAME)
+ connection.numberOfLevels.value = event.numberOfLevels
+ connection.cdmaRoaming.value = false
+ connection.connectionInfo.value = event.toMobileConnectionModel()
+ Log.e("CCS", "output connection info = ${connection.connectionInfo.value}")
+ }
+
+ private fun maybeRemoveSubscription(subId: Int?) {
if (_subscriptions.value.isEmpty()) {
// Nothing to do here
return
}
- val subId =
- state.subId
+ val finalSubId =
+ subId
?: run {
// For sake of usability, we can allow for no subId arg if there is only one
// subscription
@@ -223,7 +295,21 @@ constructor(
_subscriptions.value[0].subscriptionId
}
- removeSubscription(subId)
+ removeSubscription(finalSubId)
+ }
+
+ private fun disableCarrierMerged() {
+ val currentCarrierMergedSubId = carrierMergedSubId ?: return
+
+ // If this sub ID was previously not carrier merged, we should reset it to its previous
+ // connection.
+ val lastMobileState = connectionRepoCache[carrierMergedSubId]?.lastMobileState
+ if (lastMobileState != null) {
+ processEnabledMobileState(lastMobileState)
+ } else {
+ // Otherwise, just remove the subscription entirely
+ removeSubscription(currentCarrierMergedSubId)
+ }
}
private fun removeSubscription(subId: Int) {
@@ -251,6 +337,10 @@ constructor(
)
}
+ private fun FakeWifiEventModel.CarrierMerged.toMobileConnectionModel(): MobileConnectionModel {
+ return createCarrierMergedConnectionModel(this.level)
+ }
+
private fun SignalIcon.MobileIconGroup?.toResolvedNetworkType(): ResolvedNetworkType {
val key = mobileMappingsReverseLookup.value[this] ?: "dis"
return DefaultNetworkType(key)
@@ -260,9 +350,17 @@ constructor(
private const val TAG = "DemoMobileConnectionsRepo"
private const val DEFAULT_SUB_ID = 1
+
+ private const val CARRIER_MERGED_NAME = "Carrier Merged Network"
}
}
+class CacheContainer(
+ var repo: DemoMobileConnectionRepository,
+ /** The last received [Mobile] event. Used when switching from carrier merged back to mobile. */
+ var lastMobileState: Mobile?,
+)
+
class DemoMobileConnectionRepository(
override val subId: Int,
override val tableLogBuffer: TableLogBuffer,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
new file mode 100644
index 000000000000..c783b12e0c0b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
+
+import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
+import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
+import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
+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.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * A repository implementation for a carrier merged (aka VCN) network. A carrier merged network is
+ * delivered to SysUI as a wifi network (see [WifiNetworkModel.CarrierMerged], but is visually
+ * displayed as a mobile network triangle.
+ *
+ * See [android.net.wifi.WifiInfo.isCarrierMerged] for more information.
+ *
+ * See [MobileConnectionRepositoryImpl] for a repository implementation of a typical mobile
+ * connection.
+ */
+class CarrierMergedConnectionRepository(
+ override val subId: Int,
+ override val tableLogBuffer: TableLogBuffer,
+ defaultNetworkName: NetworkNameModel,
+ @Application private val scope: CoroutineScope,
+ val wifiRepository: WifiRepository,
+) : MobileConnectionRepository {
+
+ /**
+ * Outputs the carrier merged network to use, or null if we don't have a valid carrier merged
+ * network.
+ */
+ private val network: Flow<WifiNetworkModel.CarrierMerged?> =
+ combine(
+ wifiRepository.isWifiEnabled,
+ wifiRepository.isWifiDefault,
+ wifiRepository.wifiNetwork,
+ ) { isEnabled, isDefault, network ->
+ when {
+ !isEnabled -> null
+ !isDefault -> null
+ network !is WifiNetworkModel.CarrierMerged -> null
+ network.subscriptionId != subId -> {
+ Log.w(
+ TAG,
+ "Connection repo subId=$subId " +
+ "does not equal wifi repo subId=${network.subscriptionId}; " +
+ "not showing carrier merged"
+ )
+ null
+ }
+ else -> network
+ }
+ }
+
+ override val connectionInfo: StateFlow<MobileConnectionModel> =
+ network
+ .map { it.toMobileConnectionModel() }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), MobileConnectionModel())
+
+ // TODO(b/238425913): Add logging to this class.
+ // TODO(b/238425913): Make sure SignalStrength.getEmptyState is used when appropriate.
+
+ // Carrier merged is never roaming.
+ override val cdmaRoaming: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow()
+
+ // TODO(b/238425913): Fetch the carrier merged network name.
+ override val networkName: StateFlow<NetworkNameModel> =
+ flowOf(defaultNetworkName)
+ .stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
+
+ override val numberOfLevels: StateFlow<Int> =
+ wifiRepository.wifiNetwork
+ .map {
+ if (it is WifiNetworkModel.CarrierMerged) {
+ it.numberOfLevels
+ } else {
+ DEFAULT_NUM_LEVELS
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS)
+
+ override val dataEnabled: StateFlow<Boolean> = wifiRepository.isWifiEnabled
+
+ private fun WifiNetworkModel.CarrierMerged?.toMobileConnectionModel(): MobileConnectionModel {
+ if (this == null) {
+ return MobileConnectionModel()
+ }
+
+ return createCarrierMergedConnectionModel(level)
+ }
+
+ companion object {
+ /**
+ * Creates an instance of [MobileConnectionModel] that represents a carrier merged network
+ * with the given [level].
+ */
+ fun createCarrierMergedConnectionModel(level: Int): MobileConnectionModel {
+ return MobileConnectionModel(
+ primaryLevel = level,
+ cdmaLevel = level,
+ // A [WifiNetworkModel.CarrierMerged] instance is always connected.
+ // (A [WifiNetworkModel.Inactive] represents a disconnected network.)
+ dataConnectionState = DataConnectionState.Connected,
+ // TODO(b/238425913): This should come from [WifiRepository.wifiActivity].
+ dataActivityDirection =
+ DataActivityModel(
+ hasActivityIn = false,
+ hasActivityOut = false,
+ ),
+ resolvedNetworkType = ResolvedNetworkType.CarrierMergedNetworkType,
+ // Carrier merged is never roaming
+ isRoaming = false,
+
+ // TODO(b/238425913): Verify that these fields never change for carrier merged.
+ isEmergencyOnly = false,
+ operatorAlphaShort = null,
+ isInService = true,
+ isGsm = false,
+ carrierNetworkChangeActive = false,
+ )
+ }
+ }
+
+ @SysUISingleton
+ class Factory
+ @Inject
+ constructor(
+ @Application private val scope: CoroutineScope,
+ private val wifiRepository: WifiRepository,
+ ) {
+ fun build(
+ subId: Int,
+ mobileLogger: TableLogBuffer,
+ defaultNetworkName: NetworkNameModel,
+ ): MobileConnectionRepository {
+ return CarrierMergedConnectionRepository(
+ subId,
+ mobileLogger,
+ defaultNetworkName,
+ scope,
+ wifiRepository,
+ )
+ }
+ }
+}
+
+private const val TAG = "CarrierMergedConnectionRepository"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
new file mode 100644
index 000000000000..0f30ae249c31
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
+
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * A repository that fully implements a mobile connection.
+ *
+ * This connection could either be a typical mobile connection (see [MobileConnectionRepositoryImpl]
+ * or a carrier merged connection (see [CarrierMergedConnectionRepository]). This repository
+ * switches between the two types of connections based on whether the connection is currently
+ * carrier merged (see [setIsCarrierMerged]).
+ */
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+@OptIn(ExperimentalCoroutinesApi::class)
+class FullMobileConnectionRepository(
+ override val subId: Int,
+ startingIsCarrierMerged: Boolean,
+ override val tableLogBuffer: TableLogBuffer,
+ private val defaultNetworkName: NetworkNameModel,
+ private val networkNameSeparator: String,
+ private val globalMobileDataSettingChangedEvent: Flow<Unit>,
+ @Application scope: CoroutineScope,
+ private val mobileRepoFactory: MobileConnectionRepositoryImpl.Factory,
+ private val carrierMergedRepoFactory: CarrierMergedConnectionRepository.Factory,
+) : MobileConnectionRepository {
+ /**
+ * Sets whether this connection is a typical mobile connection or a carrier merged connection.
+ */
+ fun setIsCarrierMerged(isCarrierMerged: Boolean) {
+ _isCarrierMerged.value = isCarrierMerged
+ }
+
+ /**
+ * Returns true if this repo is currently for a carrier merged connection and false otherwise.
+ */
+ @VisibleForTesting fun getIsCarrierMerged() = _isCarrierMerged.value
+
+ private val _isCarrierMerged = MutableStateFlow(startingIsCarrierMerged)
+ private val isCarrierMerged: StateFlow<Boolean> =
+ _isCarrierMerged
+ .logDiffsForTable(
+ tableLogBuffer,
+ columnPrefix = "",
+ columnName = "isCarrierMerged",
+ initialValue = startingIsCarrierMerged,
+ )
+ .stateIn(scope, SharingStarted.WhileSubscribed(), startingIsCarrierMerged)
+
+ private val mobileRepo: MobileConnectionRepository by lazy {
+ mobileRepoFactory.build(
+ subId,
+ tableLogBuffer,
+ defaultNetworkName,
+ networkNameSeparator,
+ globalMobileDataSettingChangedEvent,
+ )
+ }
+
+ private val carrierMergedRepo: MobileConnectionRepository by lazy {
+ carrierMergedRepoFactory.build(subId, tableLogBuffer, defaultNetworkName)
+ }
+
+ @VisibleForTesting
+ internal val activeRepo: StateFlow<MobileConnectionRepository> = run {
+ val initial =
+ if (startingIsCarrierMerged) {
+ carrierMergedRepo
+ } else {
+ mobileRepo
+ }
+
+ this.isCarrierMerged
+ .mapLatest { isCarrierMerged ->
+ if (isCarrierMerged) {
+ carrierMergedRepo
+ } else {
+ mobileRepo
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
+ }
+
+ override val cdmaRoaming =
+ activeRepo
+ .flatMapLatest { it.cdmaRoaming }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.cdmaRoaming.value)
+
+ override val connectionInfo =
+ activeRepo
+ .flatMapLatest { it.connectionInfo }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.connectionInfo.value)
+
+ override val dataEnabled =
+ activeRepo
+ .flatMapLatest { it.dataEnabled }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.dataEnabled.value)
+
+ override val numberOfLevels =
+ activeRepo
+ .flatMapLatest { it.numberOfLevels }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.numberOfLevels.value)
+
+ override val networkName =
+ activeRepo
+ .flatMapLatest { it.networkName }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.networkName.value)
+
+ class Factory
+ @Inject
+ constructor(
+ @Application private val scope: CoroutineScope,
+ private val logFactory: TableLogBufferFactory,
+ private val mobileRepoFactory: MobileConnectionRepositoryImpl.Factory,
+ private val carrierMergedRepoFactory: CarrierMergedConnectionRepository.Factory,
+ ) {
+ fun build(
+ subId: Int,
+ startingIsCarrierMerged: Boolean,
+ defaultNetworkName: NetworkNameModel,
+ networkNameSeparator: String,
+ globalMobileDataSettingChangedEvent: Flow<Unit>,
+ ): FullMobileConnectionRepository {
+ val mobileLogger =
+ logFactory.getOrCreate(tableBufferLogName(subId), MOBILE_CONNECTION_BUFFER_SIZE)
+
+ return FullMobileConnectionRepository(
+ subId,
+ startingIsCarrierMerged,
+ mobileLogger,
+ defaultNetworkName,
+ networkNameSeparator,
+ globalMobileDataSettingChangedEvent,
+ scope,
+ mobileRepoFactory,
+ carrierMergedRepoFactory,
+ )
+ }
+
+ companion object {
+ /** The buffer size to use for logging. */
+ const val MOBILE_CONNECTION_BUFFER_SIZE = 100
+
+ /** Returns a log buffer name for a mobile connection with the given [subId]. */
+ fun tableBufferLogName(subId: Int): String = "MobileConnectionLog [$subId]"
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 4e42f9b31e5c..3f2ce4000ff1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -38,7 +38,6 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
@@ -70,6 +69,10 @@ import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
+/**
+ * A repository implementation for a typical mobile connection (as opposed to a carrier merged
+ * connection -- see [CarrierMergedConnectionRepository]).
+ */
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
class MobileConnectionRepositoryImpl(
@@ -298,18 +301,16 @@ class MobileConnectionRepositoryImpl(
private val logger: ConnectivityPipelineLogger,
private val globalSettings: GlobalSettings,
private val mobileMappingsProxy: MobileMappingsProxy,
- private val logFactory: TableLogBufferFactory,
@Background private val bgDispatcher: CoroutineDispatcher,
@Application private val scope: CoroutineScope,
) {
fun build(
subId: Int,
+ mobileLogger: TableLogBuffer,
defaultNetworkName: NetworkNameModel,
networkNameSeparator: String,
globalMobileDataSettingChangedEvent: Flow<Unit>,
): MobileConnectionRepository {
- val mobileLogger = logFactory.getOrCreate(tableBufferLogName(subId), 100)
-
return MobileConnectionRepositoryImpl(
context,
subId,
@@ -327,8 +328,4 @@ class MobileConnectionRepositoryImpl(
)
}
}
-
- companion object {
- fun tableBufferLogName(subId: Int): String = "MobileConnectionLog [$subId]"
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index c88c70064238..510482dcb21e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -35,6 +35,7 @@ import android.telephony.TelephonyCallback
import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener
import android.telephony.TelephonyManager
import androidx.annotation.VisibleForTesting
+import com.android.internal.telephony.PhoneConstants
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.MobileMappings.Config
import com.android.systemui.R
@@ -46,11 +47,13 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
+import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
+import com.android.systemui.util.kotlin.pairwiseBy
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -59,9 +62,12 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.merge
@@ -85,9 +91,14 @@ constructor(
private val context: Context,
@Background private val bgDispatcher: CoroutineDispatcher,
@Application private val scope: CoroutineScope,
- private val mobileConnectionRepositoryFactory: MobileConnectionRepositoryImpl.Factory
+ // Some "wifi networks" should be rendered as a mobile connection, which is why the wifi
+ // repository is an input to the mobile repository.
+ // See [CarrierMergedConnectionRepository] for details.
+ wifiRepository: WifiRepository,
+ private val fullMobileRepoFactory: FullMobileConnectionRepository.Factory,
) : MobileConnectionsRepository {
- private var subIdRepositoryCache: MutableMap<Int, MobileConnectionRepository> = mutableMapOf()
+ private var subIdRepositoryCache: MutableMap<Int, FullMobileConnectionRepository> =
+ mutableMapOf()
private val defaultNetworkName =
NetworkNameModel.Default(
@@ -97,30 +108,43 @@ constructor(
private val networkNameSeparator: String =
context.getString(R.string.status_bar_network_name_separator)
+ private val carrierMergedSubId: StateFlow<Int?> =
+ wifiRepository.wifiNetwork
+ .mapLatest {
+ if (it is WifiNetworkModel.CarrierMerged) {
+ it.subscriptionId
+ } else {
+ null
+ }
+ }
+ .distinctUntilChanged()
+ .stateIn(scope, started = SharingStarted.WhileSubscribed(), null)
+
+ private val mobileSubscriptionsChangeEvent: Flow<Unit> = conflatedCallbackFlow {
+ val callback =
+ object : SubscriptionManager.OnSubscriptionsChangedListener() {
+ override fun onSubscriptionsChanged() {
+ trySend(Unit)
+ }
+ }
+
+ subscriptionManager.addOnSubscriptionsChangedListener(
+ bgDispatcher.asExecutor(),
+ callback,
+ )
+
+ awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) }
+ }
+
/**
* State flow that emits the set of mobile data subscriptions, each represented by its own
- * [SubscriptionInfo]. We probably only need the [SubscriptionInfo.getSubscriptionId] of each
- * info object, but for now we keep track of the infos themselves.
+ * [SubscriptionModel].
*/
override val subscriptions: StateFlow<List<SubscriptionModel>> =
- conflatedCallbackFlow {
- val callback =
- object : SubscriptionManager.OnSubscriptionsChangedListener() {
- override fun onSubscriptionsChanged() {
- trySend(Unit)
- }
- }
-
- subscriptionManager.addOnSubscriptionsChangedListener(
- bgDispatcher.asExecutor(),
- callback,
- )
-
- awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) }
- }
+ merge(mobileSubscriptionsChangeEvent, carrierMergedSubId)
.mapLatest { fetchSubscriptionsList().map { it.toSubscriptionModel() } }
.logInputChange(logger, "onSubscriptionsChanged")
- .onEach { infos -> dropUnusedReposFromCache(infos) }
+ .onEach { infos -> updateRepos(infos) }
.stateIn(scope, started = SharingStarted.WhileSubscribed(), listOf())
/** StateFlow that keeps track of the current active mobile data subscription */
@@ -140,10 +164,24 @@ constructor(
.logInputChange(logger, "onActiveDataSubscriptionIdChanged")
.stateIn(scope, started = SharingStarted.WhileSubscribed(), INVALID_SUBSCRIPTION_ID)
- private val defaultDataSubIdChangedEvent =
+ private val defaultDataSubIdChangeEvent: MutableSharedFlow<Unit> =
+ MutableSharedFlow(extraBufferCapacity = 1)
+
+ override val defaultDataSubId: StateFlow<Int> =
broadcastDispatcher
- .broadcastFlow(IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED))
+ .broadcastFlow(
+ IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
+ ) { intent, _ ->
+ intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID)
+ }
+ .distinctUntilChanged()
.logInputChange(logger, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED")
+ .onEach { defaultDataSubIdChangeEvent.tryEmit(Unit) }
+ .stateIn(
+ scope,
+ SharingStarted.WhileSubscribed(),
+ SubscriptionManager.getDefaultDataSubscriptionId()
+ )
private val carrierConfigChangedEvent =
broadcastDispatcher
@@ -151,7 +189,7 @@ constructor(
.logInputChange(logger, "ACTION_CARRIER_CONFIG_CHANGED")
override val defaultDataSubRatConfig: StateFlow<Config> =
- merge(defaultDataSubIdChangedEvent, carrierConfigChangedEvent)
+ merge(defaultDataSubIdChangeEvent, carrierConfigChangedEvent)
.mapLatest { Config.readConfig(context) }
.distinctUntilChanged()
.logInputChange(logger, "defaultDataSubRatConfig")
@@ -173,7 +211,7 @@ constructor(
.distinctUntilChanged()
.logInputChange(logger, "defaultMobileIconGroup")
- override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
+ override fun getRepoForSubId(subId: Int): FullMobileConnectionRepository {
if (!isValidSubId(subId)) {
throw IllegalArgumentException(
"subscriptionId $subId is not in the list of valid subscriptions"
@@ -239,6 +277,35 @@ constructor(
.logInputChange(logger, "defaultMobileNetworkConnectivity")
.stateIn(scope, SharingStarted.WhileSubscribed(), MobileConnectivityModel())
+ /**
+ * Flow that tracks the active mobile data subscriptions. Emits `true` whenever the active data
+ * subscription Id changes but the subscription group remains the same. In these cases, we want
+ * to retain the previous subscription's validation status for up to 2s to avoid flickering the
+ * icon.
+ *
+ * TODO(b/265164432): we should probably expose all change events, not just same group
+ */
+ @SuppressLint("MissingPermission")
+ override val activeSubChangedInGroupEvent =
+ flow {
+ activeMobileDataSubscriptionId.pairwiseBy { prevVal: Int, newVal: Int ->
+ if (!defaultMobileNetworkConnectivity.value.isValidated) {
+ return@pairwiseBy
+ }
+ val prevSub = subscriptionManager.getActiveSubscriptionInfo(prevVal)
+ val nextSub = subscriptionManager.getActiveSubscriptionInfo(newVal)
+
+ if (prevSub == null || nextSub == null) {
+ return@pairwiseBy
+ }
+
+ if (prevSub.groupUuid != null && prevSub.groupUuid == nextSub.groupUuid) {
+ emit(Unit)
+ }
+ }
+ }
+ .flowOn(bgDispatcher)
+
private fun isValidSubId(subId: Int): Boolean {
subscriptions.value.forEach {
if (it.subscriptionId == subId) {
@@ -251,15 +318,27 @@ constructor(
@VisibleForTesting fun getSubIdRepoCache() = subIdRepositoryCache
- private fun createRepositoryForSubId(subId: Int): MobileConnectionRepository {
- return mobileConnectionRepositoryFactory.build(
+ private fun createRepositoryForSubId(subId: Int): FullMobileConnectionRepository {
+ return fullMobileRepoFactory.build(
subId,
+ isCarrierMerged(subId),
defaultNetworkName,
networkNameSeparator,
globalMobileDataSettingChangedEvent,
)
}
+ private fun updateRepos(newInfos: List<SubscriptionModel>) {
+ dropUnusedReposFromCache(newInfos)
+ subIdRepositoryCache.forEach { (subId, repo) ->
+ repo.setIsCarrierMerged(isCarrierMerged(subId))
+ }
+ }
+
+ private fun isCarrierMerged(subId: Int): Boolean {
+ return subId == carrierMergedSubId.value
+ }
+
private fun dropUnusedReposFromCache(newInfos: List<SubscriptionModel>) {
// Remove any connection repository from the cache that isn't in the new set of IDs. They
// will get garbage collected once their subscribers go away
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index 9427c6b9fece..9cdff96dc7d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -18,12 +18,14 @@ package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
import android.telephony.CarrierConfigManager
import com.android.settingslib.SignalIcon.MobileIconGroup
+import com.android.settingslib.mobile.TelephonyIcons.NOT_DEFAULT_DATA
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Connected
+import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -41,12 +43,29 @@ interface MobileIconInteractor {
/** The current mobile data activity */
val activity: Flow<DataActivityModel>
- /** Only true if mobile is the default transport but is not validated, otherwise false */
- val isDefaultConnectionFailed: StateFlow<Boolean>
+ /**
+ * This bit is meant to be `true` if and only if the default network capabilities (see
+ * [android.net.ConnectivityManager.registerDefaultNetworkCallback]) result in a network that
+ * has the [android.net.NetworkCapabilities.TRANSPORT_CELLULAR] represented.
+ *
+ * Note that this differs from [isDataConnected], which is tracked by telephony and has to do
+ * with the state of using this mobile connection for data as opposed to just voice. It is
+ * possible for a mobile subscription to be connected but not be in a connected data state, and
+ * thus we wouldn't want to show the network type icon.
+ */
+ val isConnected: Flow<Boolean>
- /** True when telephony tells us that the data state is CONNECTED */
+ /**
+ * True when telephony tells us that the data state is CONNECTED. See
+ * [android.telephony.TelephonyCallback.DataConnectionStateListener] for more details. We
+ * consider this connection to be serving data, and thus want to show a network type icon, when
+ * data is connected. Other data connection states would typically cause us not to show the icon
+ */
val isDataConnected: StateFlow<Boolean>
+ /** Only true if mobile is the default transport but is not validated, otherwise false */
+ val isDefaultConnectionFailed: StateFlow<Boolean>
+
/** True if we consider this connection to be in service, i.e. can make calls */
val isInService: StateFlow<Boolean>
@@ -100,8 +119,10 @@ class MobileIconInteractorImpl(
defaultSubscriptionHasDataEnabled: StateFlow<Boolean>,
override val alwaysShowDataRatIcon: StateFlow<Boolean>,
override val alwaysUseCdmaLevel: StateFlow<Boolean>,
+ defaultMobileConnectivity: StateFlow<MobileConnectivityModel>,
defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
defaultMobileIconGroup: StateFlow<MobileIconGroup>,
+ defaultDataSubId: StateFlow<Int>,
override val isDefaultConnectionFailed: StateFlow<Boolean>,
connectionRepository: MobileConnectionRepository,
) : MobileIconInteractor {
@@ -111,8 +132,19 @@ class MobileIconInteractorImpl(
override val activity = connectionInfo.mapLatest { it.dataActivityDirection }
+ override val isConnected: Flow<Boolean> = defaultMobileConnectivity.mapLatest { it.isConnected }
+
override val isDataEnabled: StateFlow<Boolean> = connectionRepository.dataEnabled
+ private val isDefault =
+ defaultDataSubId
+ .mapLatest { connectionRepository.subId == it }
+ .stateIn(
+ scope,
+ SharingStarted.WhileSubscribed(),
+ connectionRepository.subId == defaultDataSubId.value
+ )
+
override val isDefaultDataEnabled = defaultSubscriptionHasDataEnabled
override val networkName =
@@ -137,8 +169,17 @@ class MobileIconInteractorImpl(
connectionInfo,
defaultMobileIconMapping,
defaultMobileIconGroup,
- ) { info, mapping, defaultGroup ->
- mapping[info.resolvedNetworkType.lookupKey] ?: defaultGroup
+ isDefault,
+ ) { info, mapping, defaultGroup, isDefault ->
+ if (!isDefault) {
+ return@combine NOT_DEFAULT_DATA
+ }
+
+ when (info.resolvedNetworkType) {
+ is ResolvedNetworkType.CarrierMergedNetworkType ->
+ info.resolvedNetworkType.iconGroupOverride
+ else -> mapping[info.resolvedNetworkType.lookupKey] ?: defaultGroup
+ }
}
.stateIn(scope, SharingStarted.WhileSubscribed(), defaultMobileIconGroup.value)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index 83da1dd06780..9ae38e973bd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -23,6 +23,7 @@ import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
@@ -31,14 +32,17 @@ import com.android.systemui.util.CarrierConfigTracker
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transformLatest
/**
* Business layer logic for the set of mobile subscription icons.
@@ -62,6 +66,17 @@ interface MobileIconsInteractor {
/** True if the CDMA level should be preferred over the primary level. */
val alwaysUseCdmaLevel: StateFlow<Boolean>
+ /** Tracks the subscriptionId set as the default for data connections */
+ val defaultDataSubId: StateFlow<Int>
+
+ /**
+ * The connectivity of the default mobile network. Note that this can differ from what is
+ * reported from [MobileConnectionsRepository] in some cases. E.g., when the active subscription
+ * changes but the groupUuid remains the same, we keep the old validation information for 2
+ * seconds to avoid icon flickering.
+ */
+ val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel>
+
/** The icon mapping from network type to [MobileIconGroup] for the default subscription */
val defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>
/** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */
@@ -154,6 +169,48 @@ constructor(
}
}
+ override val defaultDataSubId = mobileConnectionsRepo.defaultDataSubId
+
+ /**
+ * Copied from the old pipeline. We maintain a 2s period of time where we will keep the
+ * validated bit from the old active network (A) while data is changing to the new one (B).
+ *
+ * This condition only applies if
+ * 1. A and B are in the same subscription group (e.c. for CBRS data switching) and
+ * 2. A was validated before the switch
+ *
+ * The goal of this is to minimize the flickering in the UI of the cellular indicator
+ */
+ private val forcingCellularValidation =
+ mobileConnectionsRepo.activeSubChangedInGroupEvent
+ .filter { mobileConnectionsRepo.defaultMobileNetworkConnectivity.value.isValidated }
+ .transformLatest {
+ emit(true)
+ delay(2000)
+ emit(false)
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+ override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
+ combine(
+ mobileConnectionsRepo.defaultMobileNetworkConnectivity,
+ forcingCellularValidation,
+ ) { networkConnectivity, forceValidation ->
+ return@combine if (forceValidation) {
+ MobileConnectivityModel(
+ isValidated = true,
+ isConnected = networkConnectivity.isConnected
+ )
+ } else {
+ networkConnectivity
+ }
+ }
+ .stateIn(
+ scope,
+ SharingStarted.WhileSubscribed(),
+ mobileConnectionsRepo.defaultMobileNetworkConnectivity.value
+ )
+
/**
* Mapping from network type to [MobileIconGroup] using the config generated for the default
* subscription Id. This mapping is the same for every subscription.
@@ -207,8 +264,10 @@ constructor(
activeDataConnectionHasDataEnabled,
alwaysShowDataRatIcon,
alwaysUseCdmaLevel,
+ defaultMobileNetworkConnectivity,
defaultMobileIconMapping,
defaultMobileIconGroup,
+ defaultDataSubId,
isDefaultConnectionFailed,
mobileConnectionsRepo.getRepoForSubId(subId),
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index a2117c7df188..5e9356163e6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -102,24 +102,29 @@ constructor(
.stateIn(scope, SharingStarted.WhileSubscribed(), initial)
}
+ private val showNetworkTypeIcon: Flow<Boolean> =
+ combine(
+ iconInteractor.isDataConnected,
+ iconInteractor.isDataEnabled,
+ iconInteractor.isDefaultConnectionFailed,
+ iconInteractor.alwaysShowDataRatIcon,
+ iconInteractor.isConnected,
+ ) { dataConnected, dataEnabled, failedConnection, alwaysShow, connected ->
+ alwaysShow || (dataConnected && dataEnabled && !failedConnection && connected)
+ }
+
override val networkTypeIcon: Flow<Icon?> =
combine(
iconInteractor.networkTypeIconGroup,
- iconInteractor.isDataConnected,
- iconInteractor.isDataEnabled,
- iconInteractor.isDefaultConnectionFailed,
- iconInteractor.alwaysShowDataRatIcon,
- ) { networkTypeIconGroup, dataConnected, dataEnabled, failedConnection, alwaysShow ->
+ showNetworkTypeIcon,
+ ) { networkTypeIconGroup, shouldShow ->
val desc =
if (networkTypeIconGroup.dataContentDescription != 0)
ContentDescription.Resource(networkTypeIconGroup.dataContentDescription)
else null
val icon = Icon.Resource(networkTypeIconGroup.dataType, desc)
return@combine when {
- alwaysShow -> icon
- !dataConnected -> null
- !dataEnabled -> null
- failedConnection -> null
+ !shouldShow -> null
else -> icon
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
index 4251d18357f7..da2daf2c55ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
@@ -16,13 +16,18 @@
package com.android.systemui.statusbar.pipeline.wifi.data.model
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import androidx.annotation.VisibleForTesting
import com.android.systemui.log.table.TableRowLogger
import com.android.systemui.log.table.Diffable
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
/** Provides information about the current wifi network. */
sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
+ // TODO(b/238425913): Have a better, more unified strategy for diff-logging instead of
+ // copy-pasting the column names for each sub-object.
+
/**
* A model representing that we couldn't fetch any wifi information.
*
@@ -41,8 +46,43 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
override fun logFull(row: TableRowLogger) {
row.logChange(COL_NETWORK_TYPE, TYPE_UNAVAILABLE)
row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
+ row.logChange(COL_SUB_ID, SUB_ID_DEFAULT)
+ row.logChange(COL_VALIDATED, false)
+ row.logChange(COL_LEVEL, LEVEL_DEFAULT)
+ row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT)
+ row.logChange(COL_SSID, null)
+ row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
+ row.logChange(COL_ONLINE_SIGN_UP, false)
+ row.logChange(COL_PASSPOINT_NAME, null)
+ }
+ }
+
+ /**
+ * A model representing that the wifi information we received was invalid in some way.
+ */
+ data class Invalid(
+ /** A description of why the wifi information was invalid. */
+ val invalidReason: String,
+ ) : WifiNetworkModel() {
+ override fun toString() = "WifiNetwork.Invalid[$invalidReason]"
+ override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
+ if (prevVal !is Invalid) {
+ logFull(row)
+ return
+ }
+
+ if (invalidReason != prevVal.invalidReason) {
+ row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE $invalidReason")
+ }
+ }
+
+ override fun logFull(row: TableRowLogger) {
+ row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE $invalidReason")
+ row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
+ row.logChange(COL_SUB_ID, SUB_ID_DEFAULT)
row.logChange(COL_VALIDATED, false)
row.logChange(COL_LEVEL, LEVEL_DEFAULT)
+ row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT)
row.logChange(COL_SSID, null)
row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
row.logChange(COL_ONLINE_SIGN_UP, false)
@@ -59,18 +99,21 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
return
}
- if (prevVal is CarrierMerged) {
- // The only difference between CarrierMerged and Inactive is the type
- row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE)
- return
- }
-
- // When changing from Active to Inactive, we need to log diffs to all the fields.
- logFullNonActiveNetwork(TYPE_INACTIVE, row)
+ // When changing to Inactive, we need to log diffs to all the fields.
+ logFull(row)
}
override fun logFull(row: TableRowLogger) {
- logFullNonActiveNetwork(TYPE_INACTIVE, row)
+ row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE)
+ row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
+ row.logChange(COL_SUB_ID, SUB_ID_DEFAULT)
+ row.logChange(COL_VALIDATED, false)
+ row.logChange(COL_LEVEL, LEVEL_DEFAULT)
+ row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT)
+ row.logChange(COL_SSID, null)
+ row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
+ row.logChange(COL_ONLINE_SIGN_UP, false)
+ row.logChange(COL_PASSPOINT_NAME, null)
}
}
@@ -80,22 +123,75 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
*
* See [android.net.wifi.WifiInfo.isCarrierMerged] for more information.
*/
- object CarrierMerged : WifiNetworkModel() {
- override fun toString() = "WifiNetwork.CarrierMerged"
+ data class CarrierMerged(
+ /**
+ * The [android.net.Network.netId] we received from
+ * [android.net.ConnectivityManager.NetworkCallback] in association with this wifi network.
+ *
+ * Importantly, **not** [android.net.wifi.WifiInfo.getNetworkId].
+ */
+ val networkId: Int,
+
+ /**
+ * The subscription ID that this connection represents.
+ *
+ * Comes from [android.net.wifi.WifiInfo.getSubscriptionId].
+ *
+ * Per that method, this value must not be [INVALID_SUBSCRIPTION_ID] (if it was invalid,
+ * then this is *not* a carrier merged network).
+ */
+ val subscriptionId: Int,
+
+ /**
+ * The signal level, guaranteed to be 0 <= level <= numberOfLevels.
+ */
+ val level: Int,
+
+ /**
+ * The maximum possible level.
+ */
+ val numberOfLevels: Int = DEFAULT_NUM_LEVELS,
+ ) : WifiNetworkModel() {
+ init {
+ require(level in MIN_VALID_LEVEL..numberOfLevels) {
+ "0 <= wifi level <= $numberOfLevels required; level was $level"
+ }
+ require(subscriptionId != INVALID_SUBSCRIPTION_ID) {
+ "subscription ID cannot be invalid"
+ }
+ }
override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
- if (prevVal is CarrierMerged) {
+ if (prevVal !is CarrierMerged) {
+ logFull(row)
return
}
- if (prevVal is Inactive) {
- // The only difference between CarrierMerged and Inactive is the type.
- row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED)
- return
+ if (prevVal.networkId != networkId) {
+ row.logChange(COL_NETWORK_ID, networkId)
}
+ if (prevVal.subscriptionId != subscriptionId) {
+ row.logChange(COL_SUB_ID, subscriptionId)
+ }
+ if (prevVal.level != level) {
+ row.logChange(COL_LEVEL, level)
+ }
+ if (prevVal.numberOfLevels != numberOfLevels) {
+ row.logChange(COL_NUM_LEVELS, numberOfLevels)
+ }
+ }
- // When changing from Active to CarrierMerged, we need to log diffs to all the fields.
- logFullNonActiveNetwork(TYPE_CARRIER_MERGED, row)
+ override fun logFull(row: TableRowLogger) {
+ row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED)
+ row.logChange(COL_NETWORK_ID, networkId)
+ row.logChange(COL_SUB_ID, subscriptionId)
+ row.logChange(COL_VALIDATED, true)
+ row.logChange(COL_LEVEL, level)
+ row.logChange(COL_NUM_LEVELS, numberOfLevels)
+ row.logChange(COL_SSID, null)
+ row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
+ row.logChange(COL_ONLINE_SIGN_UP, false)
+ row.logChange(COL_PASSPOINT_NAME, null)
}
}
@@ -137,38 +233,50 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
if (prevVal !is Active) {
- row.logChange(COL_NETWORK_TYPE, TYPE_ACTIVE)
+ logFull(row)
+ return
}
- if (prevVal !is Active || prevVal.networkId != networkId) {
+ if (prevVal.networkId != networkId) {
row.logChange(COL_NETWORK_ID, networkId)
}
- if (prevVal !is Active || prevVal.isValidated != isValidated) {
+ if (prevVal.isValidated != isValidated) {
row.logChange(COL_VALIDATED, isValidated)
}
- if (prevVal !is Active || prevVal.level != level) {
+ if (prevVal.level != level) {
row.logChange(COL_LEVEL, level)
}
- if (prevVal !is Active || prevVal.ssid != ssid) {
+ if (prevVal.ssid != ssid) {
row.logChange(COL_SSID, ssid)
}
// TODO(b/238425913): The passpoint-related values are frequently never used, so it
// would be great to not log them when they're not used.
- if (prevVal !is Active || prevVal.isPasspointAccessPoint != isPasspointAccessPoint) {
+ if (prevVal.isPasspointAccessPoint != isPasspointAccessPoint) {
row.logChange(COL_PASSPOINT_ACCESS_POINT, isPasspointAccessPoint)
}
- if (prevVal !is Active ||
- prevVal.isOnlineSignUpForPasspointAccessPoint !=
+ if (prevVal.isOnlineSignUpForPasspointAccessPoint !=
isOnlineSignUpForPasspointAccessPoint) {
row.logChange(COL_ONLINE_SIGN_UP, isOnlineSignUpForPasspointAccessPoint)
}
- if (prevVal !is Active ||
- prevVal.passpointProviderFriendlyName != passpointProviderFriendlyName) {
+ if (prevVal.passpointProviderFriendlyName != passpointProviderFriendlyName) {
row.logChange(COL_PASSPOINT_NAME, passpointProviderFriendlyName)
}
}
+ override fun logFull(row: TableRowLogger) {
+ row.logChange(COL_NETWORK_TYPE, TYPE_ACTIVE)
+ row.logChange(COL_NETWORK_ID, networkId)
+ row.logChange(COL_SUB_ID, null)
+ row.logChange(COL_VALIDATED, isValidated)
+ row.logChange(COL_LEVEL, level)
+ row.logChange(COL_NUM_LEVELS, null)
+ row.logChange(COL_SSID, ssid)
+ row.logChange(COL_PASSPOINT_ACCESS_POINT, isPasspointAccessPoint)
+ row.logChange(COL_ONLINE_SIGN_UP, isOnlineSignUpForPasspointAccessPoint)
+ row.logChange(COL_PASSPOINT_NAME, passpointProviderFriendlyName)
+ }
+
override fun toString(): String {
// Only include the passpoint-related values in the string if we have them. (Most
// networks won't have them so they'll be mostly clutter.)
@@ -189,21 +297,13 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
companion object {
@VisibleForTesting
- internal const val MIN_VALID_LEVEL = 0
- @VisibleForTesting
internal const val MAX_VALID_LEVEL = 4
}
}
- internal fun logFullNonActiveNetwork(type: String, row: TableRowLogger) {
- row.logChange(COL_NETWORK_TYPE, type)
- row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
- row.logChange(COL_VALIDATED, false)
- row.logChange(COL_LEVEL, LEVEL_DEFAULT)
- row.logChange(COL_SSID, null)
- row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
- row.logChange(COL_ONLINE_SIGN_UP, false)
- row.logChange(COL_PASSPOINT_NAME, null)
+ companion object {
+ @VisibleForTesting
+ internal const val MIN_VALID_LEVEL = 0
}
}
@@ -214,12 +314,16 @@ const val TYPE_ACTIVE = "Active"
const val COL_NETWORK_TYPE = "type"
const val COL_NETWORK_ID = "networkId"
+const val COL_SUB_ID = "subscriptionId"
const val COL_VALIDATED = "isValidated"
const val COL_LEVEL = "level"
+const val COL_NUM_LEVELS = "maxLevel"
const val COL_SSID = "ssid"
const val COL_PASSPOINT_ACCESS_POINT = "isPasspointAccessPoint"
const val COL_ONLINE_SIGN_UP = "isOnlineSignUpForPasspointAccessPoint"
const val COL_PASSPOINT_NAME = "passpointProviderFriendlyName"
val LEVEL_DEFAULT: String? = null
+val NUM_LEVELS_DEFAULT: String? = null
val NETWORK_ID_DEFAULT: String? = null
+val SUB_ID_DEFAULT: String? = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoModeWifiDataSource.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoModeWifiDataSource.kt
index c588945fbd67..caac8fa2f2c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoModeWifiDataSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoModeWifiDataSource.kt
@@ -22,6 +22,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.demomode.DemoMode.COMMAND_NETWORK
import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -43,10 +44,10 @@ constructor(
private fun Bundle.toWifiEvent(): FakeWifiEventModel? {
val wifi = getString("wifi") ?: return null
- return if (wifi == "show") {
- activeWifiEvent()
- } else {
- FakeWifiEventModel.WifiDisabled
+ return when (wifi) {
+ "show" -> activeWifiEvent()
+ "carriermerged" -> carrierMergedWifiEvent()
+ else -> FakeWifiEventModel.WifiDisabled
}
}
@@ -64,6 +65,14 @@ constructor(
)
}
+ private fun Bundle.carrierMergedWifiEvent(): FakeWifiEventModel.CarrierMerged {
+ val subId = getString("slot")?.toInt() ?: DEFAULT_CARRIER_MERGED_SUB_ID
+ val level = getString("level")?.toInt() ?: 0
+ val numberOfLevels = getString("numlevels")?.toInt() ?: DEFAULT_NUM_LEVELS
+
+ return FakeWifiEventModel.CarrierMerged(subId, level, numberOfLevels)
+ }
+
private fun String.toActivity(): Int =
when (this) {
"inout" -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT
@@ -71,4 +80,8 @@ constructor(
"out" -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT
else -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE
}
+
+ companion object {
+ const val DEFAULT_CARRIER_MERGED_SUB_ID = 10
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
index be3d7d4e65c4..e161b3e42d02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
@@ -66,6 +66,7 @@ constructor(
private fun processEvent(event: FakeWifiEventModel) =
when (event) {
is FakeWifiEventModel.Wifi -> processEnabledWifiState(event)
+ is FakeWifiEventModel.CarrierMerged -> processCarrierMergedWifiState(event)
is FakeWifiEventModel.WifiDisabled -> processDisabledWifiState()
}
@@ -85,6 +86,14 @@ constructor(
_wifiNetwork.value = event.toWifiNetworkModel()
}
+ private fun processCarrierMergedWifiState(event: FakeWifiEventModel.CarrierMerged) {
+ _isWifiEnabled.value = true
+ _isWifiDefault.value = true
+ // TODO(b/238425913): Support activity in demo mode.
+ _wifiActivity.value = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+ _wifiNetwork.value = event.toCarrierMergedModel()
+ }
+
private fun FakeWifiEventModel.Wifi.toWifiNetworkModel(): WifiNetworkModel =
WifiNetworkModel.Active(
networkId = DEMO_NET_ID,
@@ -99,6 +108,14 @@ constructor(
passpointProviderFriendlyName = null,
)
+ private fun FakeWifiEventModel.CarrierMerged.toCarrierMergedModel(): WifiNetworkModel =
+ WifiNetworkModel.CarrierMerged(
+ networkId = DEMO_NET_ID,
+ subscriptionId = subscriptionId,
+ level = level,
+ numberOfLevels = numberOfLevels,
+ )
+
companion object {
private const val DEMO_NET_ID = 1234
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/model/FakeWifiEventModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/model/FakeWifiEventModel.kt
index 2353fb82f3b1..518f8ce66d2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/model/FakeWifiEventModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/model/FakeWifiEventModel.kt
@@ -29,5 +29,11 @@ sealed interface FakeWifiEventModel {
val validated: Boolean?,
) : FakeWifiEventModel
+ data class CarrierMerged(
+ val subscriptionId: Int,
+ val level: Int,
+ val numberOfLevels: Int,
+ ) : FakeWifiEventModel
+
object WifiDisabled : FakeWifiEventModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index c47c20d280c7..d26499c18661 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -29,6 +29,7 @@ import android.net.NetworkRequest
import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
import android.net.wifi.WifiManager.TrafficStateCallback
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import com.android.settingslib.Utils
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -269,7 +270,19 @@ constructor(
wifiManager: WifiManager,
): WifiNetworkModel {
return if (wifiInfo.isCarrierMerged) {
- WifiNetworkModel.CarrierMerged
+ if (wifiInfo.subscriptionId == INVALID_SUBSCRIPTION_ID) {
+ WifiNetworkModel.Invalid(CARRIER_MERGED_INVALID_SUB_ID_REASON)
+ } else {
+ WifiNetworkModel.CarrierMerged(
+ networkId = network.getNetId(),
+ subscriptionId = wifiInfo.subscriptionId,
+ level = wifiManager.calculateSignalLevel(wifiInfo.rssi),
+ // The WiFi signal level returned by WifiManager#calculateSignalLevel start
+ // from 0, so WifiManager#getMaxSignalLevel + 1 represents the total level
+ // buckets count.
+ numberOfLevels = wifiManager.maxSignalLevel + 1,
+ )
+ }
} else {
WifiNetworkModel.Active(
network.getNetId(),
@@ -302,6 +315,9 @@ constructor(
.build()
private const val WIFI_NETWORK_CALLBACK_NAME = "wifiNetworkModel"
+
+ private const val CARRIER_MERGED_INVALID_SUB_ID_REASON =
+ "Wifi network was carrier merged but had invalid sub ID"
}
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
index 980560ab5d58..86dcd18c643c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
@@ -66,6 +66,7 @@ class WifiInteractorImpl @Inject constructor(
override val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
when (info) {
is WifiNetworkModel.Unavailable -> null
+ is WifiNetworkModel.Invalid -> null
is WifiNetworkModel.Inactive -> null
is WifiNetworkModel.CarrierMerged -> null
is WifiNetworkModel.Active -> when {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index 824b5972ba4b..95431afb71bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -83,6 +83,7 @@ constructor(
private fun WifiNetworkModel.icon(): WifiIcon {
return when (this) {
is WifiNetworkModel.Unavailable -> WifiIcon.Hidden
+ is WifiNetworkModel.Invalid -> WifiIcon.Hidden
is WifiNetworkModel.CarrierMerged -> WifiIcon.Hidden
is WifiNetworkModel.Inactive -> WifiIcon.Visible(
res = WIFI_NO_NETWORK,
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
index 14a9161ac291..5a8850a9f89b 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
@@ -52,8 +52,8 @@ constructor(
eventTimeMillis: Long,
batteryState: BatteryState
) {
- if (batteryState.isPresent) {
- stylusUsiPowerUi.updateBatteryState(batteryState)
+ if (batteryState.isPresent && batteryState.capacity > 0f) {
+ stylusUsiPowerUi.updateBatteryState(deviceId, batteryState)
}
}
@@ -61,6 +61,7 @@ constructor(
if (!featureFlags.isEnabled(Flags.ENABLE_USI_BATTERY_NOTIFICATIONS)) return
if (!hostDeviceSupportsStylusInput()) return
+ stylusUsiPowerUi.init()
stylusManager.registerCallback(this)
stylusManager.startListener()
}
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
index e8216576811a..8d5e01c5b782 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
@@ -18,17 +18,21 @@ package com.android.systemui.stylus
import android.Manifest
import android.app.PendingIntent
+import android.content.ActivityNotFoundException
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.BatteryState
import android.hardware.input.InputManager
+import android.os.Bundle
import android.os.Handler
import android.os.UserHandle
+import android.util.Log
import android.view.InputDevice
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
+import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -53,6 +57,7 @@ constructor(
// These values must only be accessed on the handler.
private var batteryCapacity = 1.0f
private var suppressed = false
+ private var inputDeviceId: Int? = null
fun init() {
val filter =
@@ -87,10 +92,12 @@ constructor(
}
}
- fun updateBatteryState(batteryState: BatteryState) {
+ fun updateBatteryState(deviceId: Int, batteryState: BatteryState) {
handler.post updateBattery@{
- if (batteryState.capacity == batteryCapacity) return@updateBattery
+ if (batteryState.capacity == batteryCapacity || batteryState.capacity <= 0f)
+ return@updateBattery
+ inputDeviceId = deviceId
batteryCapacity = batteryState.capacity
refresh()
}
@@ -150,23 +157,41 @@ constructor(
}
private fun getPendingBroadcast(action: String): PendingIntent? {
- return PendingIntent.getBroadcastAsUser(
+ return PendingIntent.getBroadcast(
context,
0,
- Intent(action),
+ Intent(action).setPackage(context.packageName),
PendingIntent.FLAG_IMMUTABLE,
- UserHandle.CURRENT
)
}
- private val receiver: BroadcastReceiver =
+ @VisibleForTesting
+ internal val receiver: BroadcastReceiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
ACTION_DISMISSED_LOW_BATTERY -> updateSuppression(true)
ACTION_CLICKED_LOW_BATTERY -> {
updateSuppression(true)
- // TODO(b/261584943): open USI device details page
+ if (inputDeviceId == null) return
+
+ val args = Bundle()
+ args.putInt(KEY_DEVICE_INPUT_ID, inputDeviceId!!)
+ try {
+ context.startActivity(
+ Intent(ACTION_STYLUS_USI_DETAILS)
+ .putExtra(KEY_SETTINGS_FRAGMENT_ARGS, args)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ )
+ } catch (e: ActivityNotFoundException) {
+ // In the rare scenario where the Settings app manifest doesn't contain
+ // the USI details activity, ignore the intent.
+ Log.e(
+ StylusUsiPowerUI::class.java.simpleName,
+ "Cannot open USI details page."
+ )
+ }
}
}
}
@@ -179,7 +204,11 @@ constructor(
private val USI_NOTIFICATION_ID = R.string.stylus_battery_low_percentage
- private const val ACTION_DISMISSED_LOW_BATTERY = "StylusUsiPowerUI.dismiss"
- private const val ACTION_CLICKED_LOW_BATTERY = "StylusUsiPowerUI.click"
+ @VisibleForTesting const val ACTION_DISMISSED_LOW_BATTERY = "StylusUsiPowerUI.dismiss"
+ @VisibleForTesting const val ACTION_CLICKED_LOW_BATTERY = "StylusUsiPowerUI.click"
+ @VisibleForTesting
+ const val ACTION_STYLUS_USI_DETAILS = "com.android.settings.STYLUS_USI_DETAILS_SETTINGS"
+ @VisibleForTesting const val KEY_DEVICE_INPUT_ID = "device_input_id"
+ @VisibleForTesting const val KEY_SETTINGS_FRAGMENT_ARGS = ":settings:show_fragment_args"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/telephony/ui/activity/SwitchToManagedProfileForCallActivity.kt b/packages/SystemUI/src/com/android/systemui/telephony/ui/activity/SwitchToManagedProfileForCallActivity.kt
new file mode 100644
index 000000000000..e092f01d19f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/telephony/ui/activity/SwitchToManagedProfileForCallActivity.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.telephony.ui.activity
+
+import android.app.ActivityOptions
+import android.content.DialogInterface
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.os.UserHandle
+import android.util.Log
+import android.view.WindowManager
+import com.android.internal.app.AlertActivity
+import com.android.systemui.R
+
+/** Dialog shown to the user to switch to managed profile for making a call using work SIM. */
+class SwitchToManagedProfileForCallActivity : AlertActivity(), DialogInterface.OnClickListener {
+ private lateinit var phoneNumber: Uri
+ private var managedProfileUserId = UserHandle.USER_NULL
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ window.addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS
+ )
+ super.onCreate(savedInstanceState)
+
+ phoneNumber = intent.getData()
+ managedProfileUserId =
+ intent.getIntExtra(
+ "android.telecom.extra.MANAGED_PROFILE_USER_ID",
+ UserHandle.USER_NULL
+ )
+
+ mAlertParams.apply {
+ mTitle = getString(R.string.call_from_work_profile_title)
+ mMessage = getString(R.string.call_from_work_profile_text)
+ mPositiveButtonText = getString(R.string.call_from_work_profile_action)
+ mNegativeButtonText = getString(R.string.call_from_work_profile_close)
+ mPositiveButtonListener = this@SwitchToManagedProfileForCallActivity
+ mNegativeButtonListener = this@SwitchToManagedProfileForCallActivity
+ }
+ setupAlert()
+ }
+
+ override fun onClick(dialog: DialogInterface?, which: Int) {
+ if (which == BUTTON_POSITIVE) {
+ switchToManagedProfile()
+ }
+ finish()
+ }
+
+ private fun switchToManagedProfile() {
+ try {
+ applicationContext.startActivityAsUser(
+ Intent(Intent.ACTION_DIAL, phoneNumber),
+ ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle(),
+ UserHandle.of(managedProfileUserId)
+ )
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to launch activity", e)
+ }
+ }
+
+ companion object {
+ private const val TAG = "SwitchToManagedProfileForCallActivity"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt
index 7726d09cf971..8214822f0335 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt
@@ -3,26 +3,43 @@ package com.android.systemui.unfold
import android.os.SystemProperties
import android.os.VibrationEffect
import android.os.Vibrator
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.updates.FoldProvider
+import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
+import java.util.concurrent.Executor
import javax.inject.Inject
-/**
- * Class that plays a haptics effect during unfolding a foldable device
- */
+/** Class that plays a haptics effect during unfolding a foldable device */
@SysUIUnfoldScope
class UnfoldHapticsPlayer
@Inject
constructor(
unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
+ foldProvider: FoldProvider,
+ @Main private val mainExecutor: Executor,
private val vibrator: Vibrator?
) : TransitionProgressListener {
+ private var isFirstAnimationAfterUnfold = false
+
init {
if (vibrator != null) {
// We don't need to remove the callback because we should listen to it
// the whole time when SystemUI process is alive
unfoldTransitionProgressProvider.addCallback(this)
}
+
+ foldProvider.registerCallback(
+ object : FoldCallback {
+ override fun onFoldUpdated(isFolded: Boolean) {
+ if (isFolded) {
+ isFirstAnimationAfterUnfold = true
+ }
+ }
+ },
+ mainExecutor
+ )
}
private var lastTransitionProgress = TRANSITION_PROGRESS_FULL_OPEN
@@ -36,6 +53,13 @@ constructor(
}
override fun onTransitionFinishing() {
+ // Run haptics only when unfolding the device (first animation after unfolding)
+ if (!isFirstAnimationAfterUnfold) {
+ return
+ }
+
+ isFirstAnimationAfterUnfold = false
+
// Run haptics only if the animation is long enough to notice
if (lastTransitionProgress < TRANSITION_NOTICEABLE_THRESHOLD) {
playHaptics()
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 523cf68891a0..0069bb545ef4 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -36,6 +36,8 @@ import android.view.SurfaceSession
import android.view.WindowManager
import android.view.WindowlessWindowManager
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.LinearLightRevealEffect
@@ -57,6 +59,7 @@ class UnfoldLightRevealOverlayAnimation
@Inject
constructor(
private val context: Context,
+ private val featureFlags: FeatureFlags,
private val deviceStateManager: DeviceStateManager,
private val contentResolver: ContentResolver,
private val displayManager: DisplayManager,
@@ -81,6 +84,7 @@ constructor(
private var scrimView: LightRevealScrim? = null
private var isFolded: Boolean = false
private var isUnfoldHandled: Boolean = true
+ private var overlayAddReason: AddOverlayReason? = null
private var currentRotation: Int = context.display!!.rotation
@@ -158,6 +162,8 @@ constructor(
ensureInBackground()
ensureOverlayRemoved()
+ overlayAddReason = reason
+
val newRoot = SurfaceControlViewHost(context, context.display!!, wwm)
val params = getLayoutParams()
val newView =
@@ -170,11 +176,7 @@ constructor(
.apply {
revealEffect = createLightRevealEffect()
isScrimOpaqueChangedListener = Consumer {}
- revealAmount =
- when (reason) {
- FOLD -> TRANSPARENT
- UNFOLD -> BLACK
- }
+ revealAmount = calculateRevealAmount()
}
newRoot.setView(newView, params)
@@ -207,6 +209,31 @@ constructor(
root = newRoot
}
+ private fun calculateRevealAmount(animationProgress: Float? = null): Float {
+ val overlayAddReason = overlayAddReason ?: UNFOLD
+
+ if (animationProgress == null) {
+ // Animation progress is unknown, calculate the initial value based on the overlay
+ // add reason
+ return when (overlayAddReason) {
+ FOLD -> TRANSPARENT
+ UNFOLD -> BLACK
+ }
+ }
+
+ val showVignetteWhenFolding =
+ featureFlags.isEnabled(Flags.ENABLE_DARK_VIGNETTE_WHEN_FOLDING)
+
+ return if (!showVignetteWhenFolding && overlayAddReason == FOLD) {
+ // Do not darken the content when SHOW_VIGNETTE_WHEN_FOLDING flag is off
+ // and we are folding the device. We still add the overlay to block touches
+ // while the animation is running but the overlay is transparent.
+ TRANSPARENT
+ } else {
+ animationProgress
+ }
+ }
+
private fun getLayoutParams(): WindowManager.LayoutParams {
val params: WindowManager.LayoutParams = WindowManager.LayoutParams()
@@ -259,7 +286,7 @@ constructor(
private inner class TransitionListener : TransitionProgressListener {
override fun onTransitionProgress(progress: Float) {
- executeInBackground { scrimView?.revealAmount = progress }
+ executeInBackground { scrimView?.revealAmount = calculateRevealAmount(progress) }
}
override fun onTransitionFinished() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 84f6d913b310..075ef9df9664 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -405,6 +405,13 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
}
@Test
+ public void onBouncerVisibilityChanged_resetsScale() {
+ mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.INVISIBLE);
+
+ verify(mView).resetScale();
+ }
+
+ @Test
public void onStartingToHide_sideFpsHintShown_sideFpsHintHidden() {
setupGetSecurityView();
setupConditionsToEnableSideFpsHint();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 36ed669e299c..1bbc19931c21 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -49,6 +49,8 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
+import android.window.BackEvent;
+import android.window.OnBackAnimationCallback;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.test.filters.SmallTest;
@@ -357,6 +359,27 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
}
+ @Test
+ public void testPlayBackAnimation() {
+ OnBackAnimationCallback backCallback = mKeyguardSecurityContainer.getBackCallback();
+ backCallback.onBackStarted(createBackEvent(0, 0));
+ mKeyguardSecurityContainer.getBackCallback().onBackProgressed(
+ createBackEvent(0, 1));
+ assertThat(mKeyguardSecurityContainer.getScaleX()).isEqualTo(
+ KeyguardSecurityContainer.MIN_BACK_SCALE);
+ assertThat(mKeyguardSecurityContainer.getScaleY()).isEqualTo(
+ KeyguardSecurityContainer.MIN_BACK_SCALE);
+
+ // reset scale
+ mKeyguardSecurityContainer.resetScale();
+ assertThat(mKeyguardSecurityContainer.getScaleX()).isEqualTo(1);
+ assertThat(mKeyguardSecurityContainer.getScaleY()).isEqualTo(1);
+ }
+
+ private BackEvent createBackEvent(float touchX, float progress) {
+ return new BackEvent(0, 0, progress, BackEvent.EDGE_LEFT);
+ }
+
private Configuration configuration(@Configuration.Orientation int orientation) {
Configuration config = new Configuration();
config.orientation = orientation;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index df6ce2fec5e6..df6752a2b69d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -32,6 +32,9 @@ import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELL
import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
import static com.google.common.truth.Truth.assertThat;
@@ -125,6 +128,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings;
@@ -194,6 +198,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Mock
private DevicePolicyManager mDevicePolicyManager;
@Mock
+ private DevicePostureController mDevicePostureController;
+ @Mock
private IDreamManager mDreamManager;
@Mock
private KeyguardBypassController mKeyguardBypassController;
@@ -301,6 +307,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
.thenReturn(new ServiceState());
when(mLockPatternUtils.getLockSettings()).thenReturn(mLockSettings);
when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
+ when(mDevicePostureController.getDevicePosture()).thenReturn(DEVICE_POSTURE_UNKNOWN);
mMockitoSession = ExtendedMockito.mockitoSession()
.spyStatic(SubscriptionManager.class)
@@ -312,6 +319,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
when(mUserTracker.getUserId()).thenReturn(mCurrentUserId);
ExtendedMockito.doReturn(mActivityService).when(ActivityManager::getService);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.systemui.R.integer.config_face_auth_supported_posture,
+ DEVICE_POSTURE_UNKNOWN);
mFaceWakeUpTriggersConfig = new FaceWakeUpTriggersConfig(
mContext.getResources(),
mGlobalSettings,
@@ -2242,6 +2252,54 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
eq(true));
}
+ @Test
+ public void testShouldListenForFace_withAuthSupportPostureConfig_returnsTrue()
+ throws RemoteException {
+ mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_CLOSED;
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ supportsFaceDetection();
+
+ deviceInPostureStateOpened();
+ mTestableLooper.processAllMessages();
+ // Should not listen for face when posture state in DEVICE_POSTURE_OPENED
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ deviceInPostureStateClosed();
+ mTestableLooper.processAllMessages();
+ // Should listen for face when posture state in DEVICE_POSTURE_CLOSED
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testShouldListenForFace_withoutAuthSupportPostureConfig_returnsTrue()
+ throws RemoteException {
+ mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_UNKNOWN;
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ supportsFaceDetection();
+
+ deviceInPostureStateClosed();
+ mTestableLooper.processAllMessages();
+ // Whether device in any posture state, always listen for face
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ deviceInPostureStateOpened();
+ mTestableLooper.processAllMessages();
+ // Whether device in any posture state, always listen for face
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
private void userDeviceLockDown() {
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
when(mStrongAuthTracker.getStrongAuthForUser(mCurrentUserId))
@@ -2321,6 +2379,14 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
.onAuthenticationAcquired(FINGERPRINT_ACQUIRED_START);
}
+ private void deviceInPostureStateOpened() {
+ mKeyguardUpdateMonitor.mPostureCallback.onPostureChanged(DEVICE_POSTURE_OPENED);
+ }
+
+ private void deviceInPostureStateClosed() {
+ mKeyguardUpdateMonitor.mPostureCallback.onPostureChanged(DEVICE_POSTURE_CLOSED);
+ }
+
private void successfulFingerprintAuth() {
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
.onAuthenticationSucceeded(
@@ -2462,7 +2528,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mPowerManager, mTrustManager, mSubscriptionManager, mUserManager,
mDreamManager, mDevicePolicyManager, mSensorPrivacyManager, mTelephonyManager,
mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager,
- mFaceWakeUpTriggersConfig, Optional.of(mInteractiveToAuthProvider));
+ mFaceWakeUpTriggersConfig, mDevicePostureController,
+ Optional.of(mInteractiveToAuthProvider));
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index 7c1e384f8c30..cac4a0e5432c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -12,11 +12,13 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.WindowManager
+import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.policy.DecorView
import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertNotNull
@@ -205,25 +207,74 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
verify(interactionJankMonitor).end(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN)
}
+ @Test
+ fun testAnimationDoesNotChangeLaunchableViewVisibility_viewVisible() {
+ val touchSurface = createTouchSurface()
+
+ // View is VISIBLE when starting the animation.
+ runOnMainThreadAndWaitForIdleSync { touchSurface.visibility = View.VISIBLE }
+
+ // View is invisible while the dialog is shown.
+ val dialog = showDialogFromView(touchSurface)
+ assertThat(touchSurface.visibility).isEqualTo(View.INVISIBLE)
+
+ // View is visible again when the dialog is dismissed.
+ runOnMainThreadAndWaitForIdleSync { dialog.dismiss() }
+ assertThat(touchSurface.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun testAnimationDoesNotChangeLaunchableViewVisibility_viewInvisible() {
+ val touchSurface = createTouchSurface()
+
+ // View is INVISIBLE when starting the animation.
+ runOnMainThreadAndWaitForIdleSync { touchSurface.visibility = View.INVISIBLE }
+
+ // View is INVISIBLE while the dialog is shown.
+ val dialog = showDialogFromView(touchSurface)
+ assertThat(touchSurface.visibility).isEqualTo(View.INVISIBLE)
+
+ // View is invisible like it was before showing the dialog.
+ runOnMainThreadAndWaitForIdleSync { dialog.dismiss() }
+ assertThat(touchSurface.visibility).isEqualTo(View.INVISIBLE)
+ }
+
+ @Test
+ fun testAnimationDoesNotChangeLaunchableViewVisibility_viewVisibleThenGone() {
+ val touchSurface = createTouchSurface()
+
+ // View is VISIBLE when starting the animation.
+ runOnMainThreadAndWaitForIdleSync { touchSurface.visibility = View.VISIBLE }
+
+ // View is INVISIBLE while the dialog is shown.
+ val dialog = showDialogFromView(touchSurface)
+ assertThat(touchSurface.visibility).isEqualTo(View.INVISIBLE)
+
+ // Some external call makes the View GONE. It remains INVISIBLE while the dialog is shown,
+ // as all visibility changes should be blocked.
+ runOnMainThreadAndWaitForIdleSync { touchSurface.visibility = View.GONE }
+ assertThat(touchSurface.visibility).isEqualTo(View.INVISIBLE)
+
+ // View is restored to GONE once the dialog is dismissed.
+ runOnMainThreadAndWaitForIdleSync { dialog.dismiss() }
+ assertThat(touchSurface.visibility).isEqualTo(View.GONE)
+ }
+
private fun createAndShowDialog(
animator: DialogLaunchAnimator = dialogLaunchAnimator,
): TestDialog {
val touchSurface = createTouchSurface()
- return runOnMainThreadAndWaitForIdleSync {
- val dialog = TestDialog(context)
- animator.showFromView(dialog, touchSurface)
- dialog
- }
+ return showDialogFromView(touchSurface, animator)
}
private fun createTouchSurface(): View {
return runOnMainThreadAndWaitForIdleSync {
val touchSurfaceRoot = LinearLayout(context)
- val touchSurface = View(context)
+ val touchSurface = TouchSurfaceView(context)
touchSurfaceRoot.addView(touchSurface)
// We need to attach the root to the window manager otherwise the exit animation will
- // be skipped
+ // be skipped.
ViewUtils.attachView(touchSurfaceRoot)
attachedViews.add(touchSurfaceRoot)
@@ -231,6 +282,17 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
}
}
+ private fun showDialogFromView(
+ touchSurface: View,
+ animator: DialogLaunchAnimator = dialogLaunchAnimator,
+ ): TestDialog {
+ return runOnMainThreadAndWaitForIdleSync {
+ val dialog = TestDialog(context)
+ animator.showFromView(dialog, touchSurface)
+ dialog
+ }
+ }
+
private fun createDialogAndShowFromDialog(animateFrom: Dialog): TestDialog {
return runOnMainThreadAndWaitForIdleSync {
val dialog = TestDialog(context)
@@ -248,6 +310,22 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
return result
}
+ private class TouchSurfaceView(context: Context) : FrameLayout(context), LaunchableView {
+ private val delegate =
+ LaunchableViewDelegate(
+ this,
+ superSetVisibility = { super.setVisibility(it) },
+ )
+
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {
+ delegate.setShouldBlockVisibilityChanges(block)
+ }
+
+ override fun setVisibility(visibility: Int) {
+ delegate.setVisibility(visibility)
+ }
+ }
+
private class TestDialog(context: Context) : Dialog(context) {
companion object {
const val DIALOG_WIDTH = 100
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index 3c40835fe59d..b92c5d039d45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -53,6 +53,7 @@ import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.SysuiTestableContext
import com.android.systemui.dump.DumpManager
import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.util.concurrency.FakeExecutor
@@ -106,8 +107,7 @@ class SideFpsControllerTest : SysuiTestCase() {
enum class DeviceConfig {
X_ALIGNED,
- Y_ALIGNED_UNFOLDED,
- Y_ALIGNED_FOLDED
+ Y_ALIGNED,
}
private lateinit var deviceConfig: DeviceConfig
@@ -143,6 +143,7 @@ class SideFpsControllerTest : SysuiTestCase() {
private fun testWithDisplay(
deviceConfig: DeviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation: Boolean = false,
initInfo: DisplayInfo.() -> Unit = {},
windowInsets: WindowInsets = insetsForSmallNavbar(),
block: () -> Unit
@@ -151,27 +152,21 @@ class SideFpsControllerTest : SysuiTestCase() {
when (deviceConfig) {
DeviceConfig.X_ALIGNED -> {
- displayWidth = 2560
- displayHeight = 1600
- sensorLocation = SensorLocationInternal("", 2325, 0, 0)
- boundsWidth = 160
- boundsHeight = 84
+ displayWidth = 3000
+ displayHeight = 1500
+ sensorLocation = SensorLocationInternal("", 2500, 0, 0)
+ boundsWidth = 200
+ boundsHeight = 100
}
- DeviceConfig.Y_ALIGNED_UNFOLDED -> {
- displayWidth = 2208
- displayHeight = 1840
- sensorLocation = SensorLocationInternal("", 0, 510, 0)
- boundsWidth = 110
- boundsHeight = 210
- }
- DeviceConfig.Y_ALIGNED_FOLDED -> {
- displayWidth = 1080
- displayHeight = 2100
- sensorLocation = SensorLocationInternal("", 0, 590, 0)
- boundsWidth = 110
- boundsHeight = 210
+ DeviceConfig.Y_ALIGNED -> {
+ displayWidth = 2500
+ displayHeight = 2000
+ sensorLocation = SensorLocationInternal("", 0, 300, 0)
+ boundsWidth = 100
+ boundsHeight = 200
}
}
+
indicatorBounds = Rect(0, 0, boundsWidth, boundsHeight)
displayBounds = Rect(0, 0, displayWidth, displayHeight)
var locations = listOf(sensorLocation)
@@ -194,8 +189,10 @@ class SideFpsControllerTest : SysuiTestCase() {
val displayInfo = DisplayInfo()
displayInfo.initInfo()
+
val dmGlobal = mock(DisplayManagerGlobal::class.java)
val display = Display(dmGlobal, DISPLAY_ID, displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS)
+
whenEver(dmGlobal.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(displayInfo)
whenEver(windowManager.defaultDisplay).thenReturn(display)
whenEver(windowManager.maximumWindowMetrics)
@@ -203,9 +200,15 @@ class SideFpsControllerTest : SysuiTestCase() {
whenEver(windowManager.currentWindowMetrics)
.thenReturn(WindowMetrics(displayBounds, windowInsets))
+ val sideFpsControllerContext = context.createDisplayContext(display) as SysuiTestableContext
+ sideFpsControllerContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_reverseDefaultRotation,
+ isReverseDefaultRotation
+ )
+
sideFpsController =
SideFpsController(
- context.createDisplayContext(display),
+ sideFpsControllerContext,
layoutInflater,
fingerprintManager,
windowManager,
@@ -299,108 +302,316 @@ class SideFpsControllerTest : SysuiTestCase() {
}
@Test
- fun showsWithTaskbar() =
- testWithDisplay(deviceConfig = DeviceConfig.X_ALIGNED, { rotation = Surface.ROTATION_0 }) {
- hidesWithTaskbar(visible = true)
- }
+ fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_0() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_0 }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
@Test
- fun showsWithTaskbarOnY() =
+ fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_90() =
testWithDisplay(
- deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
- { rotation = Surface.ROTATION_0 }
- ) { hidesWithTaskbar(visible = true) }
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_90 }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
@Test
- fun showsWithTaskbar90() =
- testWithDisplay(deviceConfig = DeviceConfig.X_ALIGNED, { rotation = Surface.ROTATION_90 }) {
- hidesWithTaskbar(visible = true)
- }
+ fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_180() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_180 }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+
+ @Test
+ fun showsSfpsIndicatorWithTaskbarCollapsedDownForXAlignedSensor_180() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_180 },
+ windowInsets = insetsForSmallNavbar()
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
@Test
- fun showsWithTaskbar90OnY() =
+ fun hidesSfpsIndicatorWhenOccludingTaskbarForXAlignedSensor_180() =
testWithDisplay(
- deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_180 },
+ windowInsets = insetsForLargeNavbar()
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) }
+
+ @Test
+ fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_270() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_270 }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+
+ @Test
+ fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_InReverseDefaultRotation_0() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation = true,
+ { rotation = Surface.ROTATION_0 }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+
+ @Test
+ fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_InReverseDefaultRotation_90() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation = true,
{ rotation = Surface.ROTATION_90 }
- ) { hidesWithTaskbar(visible = true) }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
@Test
- fun showsWithTaskbar180() =
+ fun showsSfpsIndicatorWithTaskbarCollapsedDownForXAlignedSensor_InReverseDefaultRotation_90() =
testWithDisplay(
deviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation = true,
+ { rotation = Surface.ROTATION_90 },
+ windowInsets = insetsForSmallNavbar()
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+
+ @Test
+ fun hidesSfpsIndicatorWhenOccludingTaskbarForXAlignedSensor_InReverseDefaultRotation_90() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation = true,
+ { rotation = Surface.ROTATION_90 },
+ windowInsets = insetsForLargeNavbar()
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) }
+
+ @Test
+ fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_InReverseDefaultRotation_180() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation = true,
{ rotation = Surface.ROTATION_180 }
- ) { hidesWithTaskbar(visible = true) }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
@Test
- fun showsWithTaskbar270OnY() =
+ fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_InReverseDefaultRotation_270() =
testWithDisplay(
- deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation = true,
{ rotation = Surface.ROTATION_270 }
- ) { hidesWithTaskbar(visible = true) }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
@Test
- fun showsWithTaskbarCollapsedDown() =
+ fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_0() =
testWithDisplay(
- deviceConfig = DeviceConfig.X_ALIGNED,
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_0 }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+
+ @Test
+ fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_90() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_90 }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+
+ @Test
+ fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_180() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_180 },
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
+ }
+
+ @Test
+ fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_270() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_270 }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+
+ @Test
+ fun showsSfpsIndicatorWithTaskbarCollapsedDownForYAlignedSensor_270() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_270 },
windowInsets = insetsForSmallNavbar()
- ) { hidesWithTaskbar(visible = true) }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
@Test
- fun showsWithTaskbarCollapsedDownOnY() =
+ fun hidesSfpsIndicatorWhenOccludingTaskbarForYAlignedSensor_270() =
testWithDisplay(
- deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_270 },
+ windowInsets = insetsForLargeNavbar()
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) }
+
+ @Test
+ fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_InReverseDefaultRotation_0() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = true,
+ { rotation = Surface.ROTATION_0 }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+
+ @Test
+ fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_InReverseDefaultRotation_90() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = true,
+ { rotation = Surface.ROTATION_90 },
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
+ }
+
+ @Test
+ fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_InReverseDefaultRotation_180() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = true,
+ { rotation = Surface.ROTATION_180 }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+
+ @Test
+ fun showsSfpsIndicatorWithTaskbarCollapsedDownForYAlignedSensor_InReverseDefaultRotation_180() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = true,
{ rotation = Surface.ROTATION_180 },
windowInsets = insetsForSmallNavbar()
- ) { hidesWithTaskbar(visible = true) }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
@Test
- fun hidesWithTaskbarDown() =
+ fun hidesSfpsIndicatorWhenOccludingTaskbarForYAlignedSensor_InReverseDefaultRotation_180() =
testWithDisplay(
- deviceConfig = DeviceConfig.X_ALIGNED,
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = true,
{ rotation = Surface.ROTATION_180 },
windowInsets = insetsForLargeNavbar()
- ) { hidesWithTaskbar(visible = false) }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) }
@Test
- fun hidesWithTaskbarDownOnY() =
+ fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_InReverseDefaultRotation_270() =
testWithDisplay(
- deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED,
- { rotation = Surface.ROTATION_270 },
- windowInsets = insetsForLargeNavbar()
- ) { hidesWithTaskbar(visible = true) }
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = true,
+ { rotation = Surface.ROTATION_270 }
+ ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
- private fun hidesWithTaskbar(visible: Boolean) {
+ private fun verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible: Boolean) {
+ sideFpsController.overlayOffsets = sensorLocation
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
executor.runAllReady()
- sideFpsController.overviewProxyListener.onTaskbarStatusUpdated(visible, false)
+ sideFpsController.overviewProxyListener.onTaskbarStatusUpdated(true, false)
executor.runAllReady()
verify(windowManager).addView(any(), any())
verify(windowManager, never()).removeView(any())
- verify(sideFpsView).visibility = if (visible) View.VISIBLE else View.GONE
+ verify(sideFpsView).visibility = if (sfpsViewVisible) View.VISIBLE else View.GONE
}
+ /**
+ * {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_0,
+ * and uses RotateUtils.rotateBounds to map to the correct indicator location given the device
+ * rotation. Assuming RotationUtils.rotateBounds works correctly, tests for indicator placement
+ * in other rotations have been omitted.
+ */
@Test
- fun testIndicatorPlacementForXAlignedSensor() =
- testWithDisplay(deviceConfig = DeviceConfig.X_ALIGNED) {
- overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ fun verifiesIndicatorPlacementForXAlignedSensor_0() {
+ testWithDisplay(
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_0 }
+ ) {
sideFpsController.overlayOffsets = sensorLocation
+
sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds)
+
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
executor.runAllReady()
verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture())
+ assertThat(overlayViewParamsCaptor.value.x).isEqualTo(sensorLocation.sensorLocationX)
+ assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0)
+ }
+ }
+ /**
+ * {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_270
+ * in reverse default rotation. It then uses RotateUtils.rotateBounds to map to the correct
+ * indicator location given the device rotation. Assuming RotationUtils.rotateBounds works
+ * correctly, tests for indicator placement in other rotations have been omitted.
+ */
+ @Test
+ fun verifiesIndicatorPlacementForXAlignedSensor_InReverseDefaultRotation_270() {
+ testWithDisplay(
+ deviceConfig = DeviceConfig.X_ALIGNED,
+ isReverseDefaultRotation = true,
+ { rotation = Surface.ROTATION_270 }
+ ) {
+ sideFpsController.overlayOffsets = sensorLocation
+
+ sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds)
+
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture())
assertThat(overlayViewParamsCaptor.value.x).isEqualTo(sensorLocation.sensorLocationX)
assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0)
}
+ }
+ /**
+ * {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_0,
+ * and uses RotateUtils.rotateBounds to map to the correct indicator location given the device
+ * rotation. Assuming RotationUtils.rotateBounds works correctly, tests for indicator placement
+ * in other rotations have been omitted.
+ */
@Test
- fun testIndicatorPlacementForYAlignedSensor() =
- testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED) {
+ fun verifiesIndicatorPlacementForYAlignedSensor_0() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_0 }
+ ) {
sideFpsController.overlayOffsets = sensorLocation
+
sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds)
+
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture())
+ assertThat(overlayViewParamsCaptor.value.x).isEqualTo(displayWidth - boundsWidth)
+ assertThat(overlayViewParamsCaptor.value.y).isEqualTo(sensorLocation.sensorLocationY)
+ }
+
+ /**
+ * {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_270
+ * in reverse default rotation. It then uses RotateUtils.rotateBounds to map to the correct
+ * indicator location given the device rotation. Assuming RotationUtils.rotateBounds works
+ * correctly, tests for indicator placement in other rotations have been omitted.
+ */
+ @Test
+ fun verifiesIndicatorPlacementForYAlignedSensor_InReverseDefaultRotation_270() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = true,
+ { rotation = Surface.ROTATION_270 }
+ ) {
+ sideFpsController.overlayOffsets = sensorLocation
+
+ sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds)
+
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
executor.runAllReady()
@@ -412,7 +623,6 @@ class SideFpsControllerTest : SysuiTestCase() {
@Test
fun hasSideFpsSensor_withSensorProps_returnsTrue() = testWithDisplay {
// By default all those tests assume the side fps sensor is available.
-
assertThat(fingerprintManager.hasSideFpsSensor()).isTrue()
}
@@ -425,7 +635,7 @@ class SideFpsControllerTest : SysuiTestCase() {
@Test
fun testLayoutParams_isKeyguardDialogType() =
- testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED) {
+ testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED) {
sideFpsController.overlayOffsets = sensorLocation
sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds)
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
@@ -440,7 +650,7 @@ class SideFpsControllerTest : SysuiTestCase() {
@Test
fun testLayoutParams_hasNoMoveAnimationWindowFlag() =
- testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED) {
+ testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED) {
sideFpsController.overlayOffsets = sensorLocation
sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds)
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
@@ -455,7 +665,7 @@ class SideFpsControllerTest : SysuiTestCase() {
@Test
fun testLayoutParams_hasTrustedOverlayWindowFlag() =
- testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED) {
+ testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED) {
sideFpsController.overlayOffsets = sensorLocation
sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds)
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 813eeeb557b8..7715f7fc5dc9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -32,9 +32,9 @@ import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
import com.android.systemui.shade.ShadeExpansionListener;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,9 +46,9 @@ import org.mockito.Captor;
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewControllerBaseTest {
- private @Captor ArgumentCaptor<KeyguardBouncer.PrimaryBouncerExpansionCallback>
+ private @Captor ArgumentCaptor<PrimaryBouncerExpansionCallback>
mBouncerExpansionCallbackCaptor;
- private KeyguardBouncer.PrimaryBouncerExpansionCallback mBouncerExpansionCallback;
+ private PrimaryBouncerExpansionCallback mBouncerExpansionCallback;
@Override
public UdfpsKeyguardViewController createUdfpsKeyguardViewController() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
index 3b4f7e10c806..9060922266c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
@@ -31,9 +31,9 @@ import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.phone.KeyguardBouncer
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.time.SystemClock
@@ -133,7 +133,7 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle
// WHEN the bouncer expansion is VISIBLE
val job = mController.listenForBouncerExpansion(this)
keyguardBouncerRepository.setPrimaryVisible(true)
- keyguardBouncerRepository.setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE)
+ keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
yield()
// THEN UDFPS shouldPauseAuth == true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
index 56043e306c16..34ddf795c7e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
@@ -39,7 +39,8 @@ class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase()
@Test
fun processTouch() {
- overlapDetector.shouldReturn = testCase.isGoodOverlap
+ overlapDetector.shouldReturn =
+ testCase.currentPointers.associate { pointer -> pointer.id to pointer.onSensor }
val actual =
underTest.processTouch(
@@ -56,7 +57,7 @@ class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase()
data class TestCase(
val event: MotionEvent,
- val isGoodOverlap: Boolean,
+ val currentPointers: List<TestPointer>,
val previousPointerOnSensorId: Int,
val overlayParams: UdfpsOverlayParams,
val expected: TouchProcessorResult,
@@ -91,28 +92,21 @@ class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase()
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_DOWN,
previousPointerOnSensorId = INVALID_POINTER_ID,
- isGoodOverlap = true,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
expectedInteractionEvent = InteractionEvent.DOWN,
- expectedPointerOnSensorId = POINTER_ID,
- ),
- genPositiveTestCases(
- motionEventAction = MotionEvent.ACTION_DOWN,
- previousPointerOnSensorId = POINTER_ID,
- isGoodOverlap = true,
- expectedInteractionEvent = InteractionEvent.DOWN,
- expectedPointerOnSensorId = POINTER_ID,
+ expectedPointerOnSensorId = POINTER_ID_1,
),
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_DOWN,
previousPointerOnSensorId = INVALID_POINTER_ID,
- isGoodOverlap = false,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
expectedInteractionEvent = InteractionEvent.UNCHANGED,
expectedPointerOnSensorId = INVALID_POINTER_ID,
),
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_DOWN,
- previousPointerOnSensorId = POINTER_ID,
- isGoodOverlap = false,
+ previousPointerOnSensorId = POINTER_ID_1,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
expectedInteractionEvent = InteractionEvent.UP,
expectedPointerOnSensorId = INVALID_POINTER_ID,
),
@@ -120,109 +114,226 @@ class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase()
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_MOVE,
previousPointerOnSensorId = INVALID_POINTER_ID,
- isGoodOverlap = true,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
expectedInteractionEvent = InteractionEvent.DOWN,
- expectedPointerOnSensorId = POINTER_ID,
+ expectedPointerOnSensorId = POINTER_ID_1,
),
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_MOVE,
- previousPointerOnSensorId = POINTER_ID,
- isGoodOverlap = true,
+ previousPointerOnSensorId = POINTER_ID_1,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
expectedInteractionEvent = InteractionEvent.UNCHANGED,
- expectedPointerOnSensorId = POINTER_ID,
+ expectedPointerOnSensorId = POINTER_ID_1,
),
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_MOVE,
previousPointerOnSensorId = INVALID_POINTER_ID,
- isGoodOverlap = false,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
expectedInteractionEvent = InteractionEvent.UNCHANGED,
expectedPointerOnSensorId = INVALID_POINTER_ID,
),
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_MOVE,
- previousPointerOnSensorId = POINTER_ID,
- isGoodOverlap = false,
+ previousPointerOnSensorId = POINTER_ID_1,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
expectedInteractionEvent = InteractionEvent.UP,
expectedPointerOnSensorId = INVALID_POINTER_ID,
),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_MOVE,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ currentPointers =
+ listOf(
+ TestPointer(id = POINTER_ID_1, onSensor = false),
+ TestPointer(id = POINTER_ID_2, onSensor = true)
+ ),
+ expectedInteractionEvent = InteractionEvent.DOWN,
+ expectedPointerOnSensorId = POINTER_ID_2,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_MOVE,
+ previousPointerOnSensorId = POINTER_ID_1,
+ currentPointers =
+ listOf(
+ TestPointer(id = POINTER_ID_1, onSensor = false),
+ TestPointer(id = POINTER_ID_2, onSensor = true)
+ ),
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = POINTER_ID_2,
+ ),
// MotionEvent.ACTION_UP
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_UP,
previousPointerOnSensorId = INVALID_POINTER_ID,
- isGoodOverlap = true,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
expectedInteractionEvent = InteractionEvent.UP,
expectedPointerOnSensorId = INVALID_POINTER_ID,
),
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_UP,
- previousPointerOnSensorId = POINTER_ID,
- isGoodOverlap = true,
+ previousPointerOnSensorId = POINTER_ID_1,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
expectedInteractionEvent = InteractionEvent.UP,
expectedPointerOnSensorId = INVALID_POINTER_ID,
),
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_UP,
previousPointerOnSensorId = INVALID_POINTER_ID,
- isGoodOverlap = false,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
expectedInteractionEvent = InteractionEvent.UNCHANGED,
expectedPointerOnSensorId = INVALID_POINTER_ID,
),
- genPositiveTestCases(
- motionEventAction = MotionEvent.ACTION_UP,
- previousPointerOnSensorId = POINTER_ID,
- isGoodOverlap = false,
- expectedInteractionEvent = InteractionEvent.UP,
- expectedPointerOnSensorId = INVALID_POINTER_ID,
- ),
// MotionEvent.ACTION_CANCEL
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_CANCEL,
previousPointerOnSensorId = INVALID_POINTER_ID,
- isGoodOverlap = true,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
expectedInteractionEvent = InteractionEvent.CANCEL,
expectedPointerOnSensorId = INVALID_POINTER_ID,
),
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_CANCEL,
- previousPointerOnSensorId = POINTER_ID,
- isGoodOverlap = true,
+ previousPointerOnSensorId = POINTER_ID_1,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
expectedInteractionEvent = InteractionEvent.CANCEL,
expectedPointerOnSensorId = INVALID_POINTER_ID,
),
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_CANCEL,
previousPointerOnSensorId = INVALID_POINTER_ID,
- isGoodOverlap = false,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
expectedInteractionEvent = InteractionEvent.CANCEL,
expectedPointerOnSensorId = INVALID_POINTER_ID,
),
genPositiveTestCases(
motionEventAction = MotionEvent.ACTION_CANCEL,
- previousPointerOnSensorId = POINTER_ID,
- isGoodOverlap = false,
+ previousPointerOnSensorId = POINTER_ID_1,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
expectedInteractionEvent = InteractionEvent.CANCEL,
expectedPointerOnSensorId = INVALID_POINTER_ID,
),
+ // MotionEvent.ACTION_POINTER_DOWN
+ genPositiveTestCases(
+ motionEventAction =
+ MotionEvent.ACTION_POINTER_DOWN +
+ (1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ currentPointers =
+ listOf(
+ TestPointer(id = POINTER_ID_1, onSensor = true),
+ TestPointer(id = POINTER_ID_2, onSensor = false)
+ ),
+ expectedInteractionEvent = InteractionEvent.DOWN,
+ expectedPointerOnSensorId = POINTER_ID_1,
+ ),
+ genPositiveTestCases(
+ motionEventAction =
+ MotionEvent.ACTION_POINTER_DOWN +
+ (1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ currentPointers =
+ listOf(
+ TestPointer(id = POINTER_ID_1, onSensor = false),
+ TestPointer(id = POINTER_ID_2, onSensor = true)
+ ),
+ expectedInteractionEvent = InteractionEvent.DOWN,
+ expectedPointerOnSensorId = POINTER_ID_2
+ ),
+ genPositiveTestCases(
+ motionEventAction =
+ MotionEvent.ACTION_POINTER_DOWN +
+ (1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
+ previousPointerOnSensorId = POINTER_ID_1,
+ currentPointers =
+ listOf(
+ TestPointer(id = POINTER_ID_1, onSensor = true),
+ TestPointer(id = POINTER_ID_2, onSensor = false)
+ ),
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = POINTER_ID_1,
+ ),
+ // MotionEvent.ACTION_POINTER_UP
+ genPositiveTestCases(
+ motionEventAction =
+ MotionEvent.ACTION_POINTER_UP +
+ (1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ currentPointers =
+ listOf(
+ TestPointer(id = POINTER_ID_1, onSensor = false),
+ TestPointer(id = POINTER_ID_2, onSensor = false)
+ ),
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = INVALID_POINTER_ID
+ ),
+ genPositiveTestCases(
+ motionEventAction =
+ MotionEvent.ACTION_POINTER_UP +
+ (1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
+ previousPointerOnSensorId = POINTER_ID_2,
+ currentPointers =
+ listOf(
+ TestPointer(id = POINTER_ID_1, onSensor = false),
+ TestPointer(id = POINTER_ID_2, onSensor = true)
+ ),
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_POINTER_UP,
+ previousPointerOnSensorId = POINTER_ID_1,
+ currentPointers =
+ listOf(
+ TestPointer(id = POINTER_ID_1, onSensor = true),
+ TestPointer(id = POINTER_ID_2, onSensor = false)
+ ),
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID
+ ),
+ genPositiveTestCases(
+ motionEventAction =
+ MotionEvent.ACTION_POINTER_UP +
+ (1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
+ previousPointerOnSensorId = POINTER_ID_1,
+ currentPointers =
+ listOf(
+ TestPointer(id = POINTER_ID_1, onSensor = true),
+ TestPointer(id = POINTER_ID_2, onSensor = false)
+ ),
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = POINTER_ID_1
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_POINTER_UP,
+ previousPointerOnSensorId = POINTER_ID_2,
+ currentPointers =
+ listOf(
+ TestPointer(id = POINTER_ID_1, onSensor = false),
+ TestPointer(id = POINTER_ID_2, onSensor = true)
+ ),
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = POINTER_ID_2
+ )
)
.flatten() +
listOf(
- // Unsupported MotionEvent actions.
- genTestCasesForUnsupportedAction(MotionEvent.ACTION_POINTER_DOWN),
- genTestCasesForUnsupportedAction(MotionEvent.ACTION_POINTER_UP),
genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_ENTER),
genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_MOVE),
- genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_EXIT),
+ genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_EXIT)
)
.flatten()
}
}
+data class TestPointer(val id: Int, val onSensor: Boolean)
+
/* Display dimensions in native resolution and natural orientation. */
private const val ROTATION_0_NATIVE_DISPLAY_WIDTH = 400
private const val ROTATION_0_NATIVE_DISPLAY_HEIGHT = 600
/* Placeholder touch parameters. */
-private const val POINTER_ID = 42
+private const val POINTER_ID_1 = 42
+private const val POINTER_ID_2 = 43
private const val NATIVE_MINOR = 2.71828f
private const val NATIVE_MAJOR = 3.14f
private const val ORIENTATION = 1.2345f
@@ -325,7 +436,7 @@ private val ROTATION_270_INPUTS =
private val MOTION_EVENT =
obtainMotionEvent(
action = 0,
- pointerId = POINTER_ID,
+ pointerId = POINTER_ID_1,
x = 0f,
y = 0f,
minor = 0f,
@@ -338,7 +449,7 @@ private val MOTION_EVENT =
/* Template [NormalizedTouchData]. */
private val NORMALIZED_TOUCH_DATA =
NormalizedTouchData(
- POINTER_ID,
+ POINTER_ID_1,
x = 0f,
y = 0f,
NATIVE_MINOR,
@@ -384,7 +495,7 @@ private data class OrientationBasedInputs(
private fun genPositiveTestCases(
motionEventAction: Int,
previousPointerOnSensorId: Int,
- isGoodOverlap: Boolean,
+ currentPointers: List<TestPointer>,
expectedInteractionEvent: InteractionEvent,
expectedPointerOnSensorId: Int
): List<SinglePointerTouchProcessorTest.TestCase> {
@@ -399,22 +510,47 @@ private fun genPositiveTestCases(
return scaleFactors.flatMap { scaleFactor ->
orientations.map { orientation ->
val overlayParams = orientation.toOverlayParams(scaleFactor)
- val nativeX = orientation.getNativeX(isGoodOverlap)
- val nativeY = orientation.getNativeY(isGoodOverlap)
+
+ val pointerProperties =
+ currentPointers
+ .map { pointer ->
+ val pp = MotionEvent.PointerProperties()
+ pp.id = pointer.id
+ pp
+ }
+ .toList()
+
+ val pointerCoords =
+ currentPointers
+ .map { pointer ->
+ val pc = MotionEvent.PointerCoords()
+ pc.x = orientation.getNativeX(pointer.onSensor) * scaleFactor
+ pc.y = orientation.getNativeY(pointer.onSensor) * scaleFactor
+ pc.touchMinor = NATIVE_MINOR * scaleFactor
+ pc.touchMajor = NATIVE_MAJOR * scaleFactor
+ pc.orientation = orientation.nativeOrientation
+ pc
+ }
+ .toList()
+
val event =
MOTION_EVENT.copy(
action = motionEventAction,
- x = nativeX * scaleFactor,
- y = nativeY * scaleFactor,
- minor = NATIVE_MINOR * scaleFactor,
- major = NATIVE_MAJOR * scaleFactor,
- orientation = orientation.nativeOrientation
+ pointerProperties = pointerProperties,
+ pointerCoords = pointerCoords
)
+
val expectedTouchData =
- NORMALIZED_TOUCH_DATA.copy(
- x = ROTATION_0_INPUTS.getNativeX(isGoodOverlap),
- y = ROTATION_0_INPUTS.getNativeY(isGoodOverlap),
- )
+ if (expectedPointerOnSensorId != INVALID_POINTER_ID) {
+ NORMALIZED_TOUCH_DATA.copy(
+ pointerId = expectedPointerOnSensorId,
+ x = ROTATION_0_INPUTS.getNativeX(isWithinSensor = true),
+ y = ROTATION_0_INPUTS.getNativeY(isWithinSensor = true)
+ )
+ } else {
+ NormalizedTouchData()
+ }
+
val expected =
TouchProcessorResult.ProcessedTouch(
event = expectedInteractionEvent,
@@ -423,7 +559,7 @@ private fun genPositiveTestCases(
)
SinglePointerTouchProcessorTest.TestCase(
event = event,
- isGoodOverlap = isGoodOverlap,
+ currentPointers = currentPointers,
previousPointerOnSensorId = previousPointerOnSensorId,
overlayParams = overlayParams,
expected = expected,
@@ -436,7 +572,7 @@ private fun genTestCasesForUnsupportedAction(
motionEventAction: Int
): List<SinglePointerTouchProcessorTest.TestCase> {
val isGoodOverlap = true
- val previousPointerOnSensorIds = listOf(INVALID_POINTER_ID, POINTER_ID)
+ val previousPointerOnSensorIds = listOf(INVALID_POINTER_ID, POINTER_ID_1)
return previousPointerOnSensorIds.map { previousPointerOnSensorId ->
val overlayParams = ROTATION_0_INPUTS.toOverlayParams(scaleFactor = 1f)
val nativeX = ROTATION_0_INPUTS.getNativeX(isGoodOverlap)
@@ -451,7 +587,7 @@ private fun genTestCasesForUnsupportedAction(
)
SinglePointerTouchProcessorTest.TestCase(
event = event,
- isGoodOverlap = isGoodOverlap,
+ currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = isGoodOverlap)),
previousPointerOnSensorId = previousPointerOnSensorId,
overlayParams = overlayParams,
expected = TouchProcessorResult.Failure(),
@@ -478,13 +614,23 @@ private fun obtainMotionEvent(
pc.touchMinor = minor
pc.touchMajor = major
pc.orientation = orientation
+ return obtainMotionEvent(action, arrayOf(pp), arrayOf(pc), time, gestureStart)
+}
+
+private fun obtainMotionEvent(
+ action: Int,
+ pointerProperties: Array<MotionEvent.PointerProperties>,
+ pointerCoords: Array<MotionEvent.PointerCoords>,
+ time: Long,
+ gestureStart: Long,
+): MotionEvent {
return MotionEvent.obtain(
gestureStart /* downTime */,
time /* eventTime */,
action /* action */,
- 1 /* pointerCount */,
- arrayOf(pp) /* pointerProperties */,
- arrayOf(pc) /* pointerCoords */,
+ pointerCoords.size /* pointerCount */,
+ pointerProperties /* pointerProperties */,
+ pointerCoords /* pointerCoords */,
0 /* metaState */,
0 /* buttonState */,
1f /* xPrecision */,
@@ -508,4 +654,19 @@ private fun MotionEvent.copy(
gestureStart: Long = this.downTime,
) = obtainMotionEvent(action, pointerId, x, y, minor, major, orientation, time, gestureStart)
+private fun MotionEvent.copy(
+ action: Int = this.action,
+ pointerProperties: List<MotionEvent.PointerProperties>,
+ pointerCoords: List<MotionEvent.PointerCoords>,
+ time: Long = this.eventTime,
+ gestureStart: Long = this.downTime
+) =
+ obtainMotionEvent(
+ action,
+ pointerProperties.toTypedArray(),
+ pointerCoords.toTypedArray(),
+ time,
+ gestureStart
+ )
+
private fun Rect.scaled(scaleFactor: Float) = Rect(this).apply { scale(scaleFactor) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
new file mode 100644
index 000000000000..3e6cc3bb4f6b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.compose
+
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import android.testing.ViewUtils
+import android.widget.FrameLayout
+import androidx.compose.ui.platform.ComposeView
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ComposeInitializerTest : SysuiTestCase() {
+ @Test
+ fun testCanAddComposeViewInInitializedWindow() {
+ if (!ComposeFacade.isComposeAvailable()) {
+ return
+ }
+
+ val root = TestWindowRoot(context)
+ try {
+ runOnMainThreadAndWaitForIdleSync { ViewUtils.attachView(root) }
+ assertThat(root.isAttachedToWindow).isTrue()
+
+ runOnMainThreadAndWaitForIdleSync { root.addView(ComposeView(context)) }
+ } finally {
+ runOnMainThreadAndWaitForIdleSync { ViewUtils.detachView(root) }
+ }
+ }
+
+ private fun runOnMainThreadAndWaitForIdleSync(f: () -> Unit) {
+ mContext.mainExecutor.execute(f)
+ waitForIdleSync()
+ }
+
+ class TestWindowRoot(context: Context) : FrameLayout(context) {
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ ComposeFacade.composeInitializer().onAttachedToWindow(this)
+ }
+
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ ComposeFacade.composeInitializer().onDetachedFromWindow(this)
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
index 0a81c38e7448..ebbe096b0da3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
@@ -269,6 +269,14 @@ class ControlsBindingControllerImplTest : SysuiTestCase() {
}
@Test
+ fun testBindServiceForPanel() {
+ controller.bindServiceForPanel(TEST_COMPONENT_NAME_1)
+ executor.runAllReady()
+
+ verify(providers[0]).bindServiceForPanel()
+ }
+
+ @Test
fun testSubscribe() {
val controlInfo1 = ControlInfo("id_1", "", "", DeviceTypes.TYPE_UNKNOWN)
val controlInfo2 = ControlInfo("id_2", "", "", DeviceTypes.TYPE_UNKNOWN)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index 1b34706bd220..25f471b0d3e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -919,6 +919,12 @@ class ControlsControllerImplTest : SysuiTestCase() {
.getFile(ControlsFavoritePersistenceWrapper.FILE_NAME, context.user.identifier)
assertThat(userStructure.file).isNotNull()
}
+
+ @Test
+ fun testBindForPanel() {
+ controller.bindComponentForPanel(TEST_COMPONENT)
+ verify(bindingController).bindServiceForPanel(TEST_COMPONENT)
+ }
}
private class DidRunRunnable() : Runnable {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
index af3f24a1c58a..da548f7ccef2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
@@ -105,6 +105,22 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() {
}
@Test
+ fun testBindForPanel() {
+ manager.bindServiceForPanel()
+ executor.runAllReady()
+ assertTrue(context.isBound(componentName))
+ }
+
+ @Test
+ fun testUnbindPanelIsUnbound() {
+ manager.bindServiceForPanel()
+ executor.runAllReady()
+ manager.unbindService()
+ executor.runAllReady()
+ assertFalse(context.isBound(componentName))
+ }
+
+ @Test
fun testNullBinding() {
val mockContext = mock(Context::class.java)
lateinit var serviceConnection: ServiceConnection
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index d172c9a2e630..edc6882e71c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -229,6 +229,15 @@ class ControlsUiControllerImplTest : SysuiTestCase() {
}
@Test
+ fun testPanelBindsForPanel() {
+ val panel = SelectedItem.PanelItem("App name", ComponentName("pkg", "cls"))
+ setUpPanel(panel)
+
+ underTest.show(parent, {}, context)
+ verify(controlsController).bindComponentForPanel(panel.componentName)
+ }
+
+ @Test
fun testPanelCallsTaskViewFactoryCreate() {
mockLayoutInflater()
val panel = SelectedItem.PanelItem("App name", ComponentName("pkg", "cls"))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index d910a275967b..e87f1042927f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -144,7 +144,7 @@ public class DozeSensorsTest extends SysuiTestCase {
@Test
public void testSensorDebounce() {
- mDozeSensors.setListening(true, true);
+ mDozeSensors.setListening(true, true, true);
mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class));
mTestableLooper.processAllMessages();
@@ -162,7 +162,7 @@ public class DozeSensorsTest extends SysuiTestCase {
@Test
public void testSetListening_firstTrue_registerSettingsObserver() {
verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt());
- mDozeSensors.setListening(true, true);
+ mDozeSensors.setListening(true, true, true);
verify(mTriggerSensor).registerSettingsObserver(any(ContentObserver.class));
}
@@ -170,8 +170,8 @@ public class DozeSensorsTest extends SysuiTestCase {
@Test
public void testSetListening_twiceTrue_onlyRegisterSettingsObserverOnce() {
verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt());
- mDozeSensors.setListening(true, true);
- mDozeSensors.setListening(true, true);
+ mDozeSensors.setListening(true, true, true);
+ mDozeSensors.setListening(true, true, true);
verify(mTriggerSensor, times(1)).registerSettingsObserver(any(ContentObserver.class));
}
@@ -196,7 +196,7 @@ public class DozeSensorsTest extends SysuiTestCase {
assertFalse(mSensorTap.mRequested);
// WHEN we're now in a low powered state
- dozeSensors.setListening(true, true, true);
+ dozeSensors.setListeningWithPowerState(true, true, true, true);
// THEN the tap sensor is registered
assertTrue(mSensorTap.mRequested);
@@ -207,12 +207,12 @@ public class DozeSensorsTest extends SysuiTestCase {
// GIVEN doze sensors enabled
when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true);
- // GIVEN a trigger sensor
+ // GIVEN a trigger sensor that's enabled by settings
Sensor mockSensor = mock(Sensor.class);
- TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+ TriggerSensor triggerSensor = mDozeSensors.createDozeSensorWithSettingEnabled(
mockSensor,
- /* settingEnabled */ true,
- /* requiresTouchScreen */ true);
+ /* settingEnabled */ true
+ );
when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor)))
.thenReturn(true);
@@ -228,12 +228,12 @@ public class DozeSensorsTest extends SysuiTestCase {
// GIVEN doze sensors enabled
when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true);
- // GIVEN a trigger sensor
+ // GIVEN a trigger sensor that's not enabled by settings
Sensor mockSensor = mock(Sensor.class);
- TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+ TriggerSensor triggerSensor = mDozeSensors.createDozeSensorWithSettingEnabled(
mockSensor,
- /* settingEnabled*/ false,
- /* requiresTouchScreen */ true);
+ /* settingEnabled*/ false
+ );
when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor)))
.thenReturn(true);
@@ -249,12 +249,12 @@ public class DozeSensorsTest extends SysuiTestCase {
// GIVEN doze sensors enabled
when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true);
- // GIVEN a trigger sensor that's
+ // GIVEN a trigger sensor that's not enabled by settings
Sensor mockSensor = mock(Sensor.class);
- TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+ TriggerSensor triggerSensor = mDozeSensors.createDozeSensorWithSettingEnabled(
mockSensor,
- /* settingEnabled*/ false,
- /* requiresTouchScreen */ true);
+ /* settingEnabled*/ false
+ );
when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor)))
.thenReturn(true);
@@ -264,7 +264,7 @@ public class DozeSensorsTest extends SysuiTestCase {
// WHEN ignoreSetting is called
triggerSensor.ignoreSetting(true);
- // THEN the sensor is registered
+ // THEN the sensor is still registered since the setting is ignore
assertTrue(triggerSensor.mRegistered);
}
@@ -275,10 +275,10 @@ public class DozeSensorsTest extends SysuiTestCase {
// GIVEN a trigger sensor
Sensor mockSensor = mock(Sensor.class);
- TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+ TriggerSensor triggerSensor = mDozeSensors.createDozeSensorWithSettingEnabled(
mockSensor,
- /* settingEnabled*/ true,
- /* requiresTouchScreen */ true);
+ /* settingEnabled*/ true
+ );
when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor)))
.thenReturn(true);
@@ -295,7 +295,7 @@ public class DozeSensorsTest extends SysuiTestCase {
// GIVEN doze sensor that supports postures
Sensor closedSensor = createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
Sensor openedSensor = createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_LIGHT);
- TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+ TriggerSensor triggerSensor = mDozeSensors.createDozeSensorForPosture(
new Sensor[] {
null /* unknown */,
closedSensor,
@@ -316,7 +316,7 @@ public class DozeSensorsTest extends SysuiTestCase {
// GIVEN doze sensor that supports postures
Sensor closedSensor = createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
Sensor openedSensor = createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_LIGHT);
- TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+ TriggerSensor triggerSensor = mDozeSensors.createDozeSensorForPosture(
new Sensor[] {
null /* unknown */,
closedSensor,
@@ -345,7 +345,7 @@ public class DozeSensorsTest extends SysuiTestCase {
// GIVEN doze sensor that supports postures
Sensor closedSensor = createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
Sensor openedSensor = createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_LIGHT);
- TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+ TriggerSensor triggerSensor = mDozeSensors.createDozeSensorForPosture(
new Sensor[] {
null /* unknown */,
closedSensor,
@@ -400,7 +400,7 @@ public class DozeSensorsTest extends SysuiTestCase {
public void testUdfpsEnrollmentChanged() throws Exception {
// GIVEN a UDFPS_LONG_PRESS trigger sensor that's not configured
Sensor mockSensor = mock(Sensor.class);
- TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+ TriggerSensor triggerSensor = mDozeSensors.createDozeSensorForPosture(
mockSensor,
REASON_SENSOR_UDFPS_LONG_PRESS,
/* configured */ false);
@@ -409,7 +409,7 @@ public class DozeSensorsTest extends SysuiTestCase {
.thenReturn(true);
// WHEN listening state is set to TRUE
- mDozeSensors.setListening(true, true);
+ mDozeSensors.setListening(true, true, true);
// THEN mRegistered is still false b/c !mConfigured
assertFalse(triggerSensor.mConfigured);
@@ -439,6 +439,35 @@ public class DozeSensorsTest extends SysuiTestCase {
}
@Test
+ public void aodOnlySensor_onlyRegisteredWhenAodSensorsIncluded() {
+ // GIVEN doze sensors enabled
+ when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true);
+
+ // GIVEN a trigger sensor that requires aod
+ Sensor mockSensor = mock(Sensor.class);
+ TriggerSensor aodOnlyTriggerSensor = mDozeSensors.createDozeSensorRequiringAod(mockSensor);
+ when(mSensorManager.requestTriggerSensor(eq(aodOnlyTriggerSensor), eq(mockSensor)))
+ .thenReturn(true);
+ mDozeSensors.addSensor(aodOnlyTriggerSensor);
+
+ // WHEN aod only sensors aren't included
+ mDozeSensors.setListening(/* listen */ true, /* includeTouchScreenSensors */true,
+ /* includeAodOnlySensors */false);
+
+ // THEN the sensor is not registered or requested
+ assertFalse(aodOnlyTriggerSensor.mRequested);
+ assertFalse(aodOnlyTriggerSensor.mRegistered);
+
+ // WHEN aod only sensors ARE included
+ mDozeSensors.setListening(/* listen */ true, /* includeTouchScreenSensors */true,
+ /* includeAodOnlySensors */true);
+
+ // THEN the sensor is registered and requested
+ assertTrue(aodOnlyTriggerSensor.mRequested);
+ assertTrue(aodOnlyTriggerSensor.mRegistered);
+ }
+
+ @Test
public void liftToWake_defaultSetting_configDefaultFalse() {
// WHEN the default lift to wake gesture setting is false
when(mResources.getBoolean(
@@ -494,8 +523,8 @@ public class DozeSensorsTest extends SysuiTestCase {
mTriggerSensors = new TriggerSensor[] {mTriggerSensor, mSensorTap};
}
- public TriggerSensor createDozeSensor(Sensor sensor, boolean settingEnabled,
- boolean requiresTouchScreen) {
+ public TriggerSensor createDozeSensorWithSettingEnabled(Sensor sensor,
+ boolean settingEnabled) {
return new TriggerSensor(/* sensor */ sensor,
/* setting name */ "test_setting",
/* settingDefault */ settingEnabled,
@@ -504,11 +533,13 @@ public class DozeSensorsTest extends SysuiTestCase {
/* reportsTouchCoordinate*/ false,
/* requiresTouchscreen */ false,
/* ignoresSetting */ false,
- requiresTouchScreen,
- /* immediatelyReRegister */ true);
+ /* requiresProx */ false,
+ /* immediatelyReRegister */ true,
+ /* requiresAod */false
+ );
}
- public TriggerSensor createDozeSensor(
+ public TriggerSensor createDozeSensorForPosture(
Sensor sensor,
int pulseReason,
boolean configured
@@ -522,15 +553,35 @@ public class DozeSensorsTest extends SysuiTestCase {
/* requiresTouchscreen */ false,
/* ignoresSetting */ false,
/* requiresTouchScreen */ false,
- /* immediatelyReRegister*/ true);
+ /* immediatelyReRegister*/ true,
+ false
+ );
}
/**
- * create a doze sensor that supports postures and is enabled
+ * Create a doze sensor that requires Aod
*/
- public TriggerSensor createDozeSensor(Sensor[] sensors, int posture) {
+ public TriggerSensor createDozeSensorRequiringAod(Sensor sensor) {
+ return new TriggerSensor(/* sensor */ sensor,
+ /* setting name */ "aod_requiring_sensor",
+ /* settingDefault */ true,
+ /* configured */ true,
+ /* pulseReason*/ 0,
+ /* reportsTouchCoordinate*/ false,
+ /* requiresTouchscreen */ false,
+ /* ignoresSetting */ false,
+ /* requiresProx */ false,
+ /* immediatelyReRegister */ true,
+ /* requiresAoD */ true
+ );
+ }
+
+ /**
+ * Create a doze sensor that supports postures and is enabled
+ */
+ public TriggerSensor createDozeSensorForPosture(Sensor[] sensors, int posture) {
return new TriggerSensor(/* sensor */ sensors,
- /* setting name */ "test_setting",
+ /* setting name */ "posture_test_setting",
/* settingDefault */ true,
/* configured */ true,
/* pulseReason*/ 0,
@@ -539,7 +590,9 @@ public class DozeSensorsTest extends SysuiTestCase {
/* ignoresSetting */ true,
/* requiresProx */ false,
/* immediatelyReRegister */ true,
- posture);
+ posture,
+ /* requiresUi */ false
+ );
}
public void addSensor(TriggerSensor sensor) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index b66a454638ac..3552399586a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -395,6 +395,14 @@ public class DozeTriggersTest extends SysuiTestCase {
verify(mAuthController).onAodInterrupt(anyInt(), anyInt(), anyFloat(), anyFloat());
}
+
+ @Test
+ public void udfpsLongPress_dozeState_notRegistered() {
+ // GIVEN device is DOZE_AOD_PAUSED
+ when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+ // beverlyt
+ }
+
private void waitForSensorManager() {
mExecutor.runAllReady();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 2799a25316d0..ff883eb16bde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -37,9 +37,9 @@ import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.complication.ComplicationHostViewController;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index 4bd53c00327f..f64179deec35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -41,11 +41,11 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -302,12 +302,13 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
final float velocityY = -1;
swipeToPosition(swipeUpPercentage, Direction.UP, velocityY);
- verify(mValueAnimatorCreator).create(eq(expansion), eq(KeyguardBouncer.EXPANSION_HIDDEN));
+ verify(mValueAnimatorCreator).create(eq(expansion),
+ eq(KeyguardBouncerConstants.EXPANSION_HIDDEN));
verify(mValueAnimator, never()).addListener(any());
verify(mFlingAnimationUtilsClosing).apply(eq(mValueAnimator),
eq(SCREEN_HEIGHT_PX * expansion),
- eq(SCREEN_HEIGHT_PX * KeyguardBouncer.EXPANSION_HIDDEN),
+ eq(SCREEN_HEIGHT_PX * KeyguardBouncerConstants.EXPANSION_HIDDEN),
eq(velocityY), eq((float) SCREEN_HEIGHT_PX));
verify(mValueAnimator).start();
verify(mUiEventLogger, never()).log(any());
@@ -324,7 +325,8 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
final float velocityY = 1;
swipeToPosition(swipeUpPercentage, Direction.UP, velocityY);
- verify(mValueAnimatorCreator).create(eq(expansion), eq(KeyguardBouncer.EXPANSION_VISIBLE));
+ verify(mValueAnimatorCreator).create(eq(expansion),
+ eq(KeyguardBouncerConstants.EXPANSION_VISIBLE));
ArgumentCaptor<AnimatorListenerAdapter> endAnimationListenerCaptor =
ArgumentCaptor.forClass(AnimatorListenerAdapter.class);
@@ -332,7 +334,7 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
AnimatorListenerAdapter endAnimationListener = endAnimationListenerCaptor.getValue();
verify(mFlingAnimationUtils).apply(eq(mValueAnimator), eq(SCREEN_HEIGHT_PX * expansion),
- eq(SCREEN_HEIGHT_PX * KeyguardBouncer.EXPANSION_VISIBLE),
+ eq(SCREEN_HEIGHT_PX * KeyguardBouncerConstants.EXPANSION_VISIBLE),
eq(velocityY), eq((float) SCREEN_HEIGHT_PX));
verify(mValueAnimator).start();
verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_SWIPED);
@@ -355,12 +357,12 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
swipeToPosition(swipeDownPercentage, Direction.DOWN, velocityY);
verify(mValueAnimatorCreator).create(eq(swipeDownPercentage),
- eq(KeyguardBouncer.EXPANSION_VISIBLE));
+ eq(KeyguardBouncerConstants.EXPANSION_VISIBLE));
verify(mValueAnimator, never()).addListener(any());
verify(mFlingAnimationUtils).apply(eq(mValueAnimator),
eq(SCREEN_HEIGHT_PX * swipeDownPercentage),
- eq(SCREEN_HEIGHT_PX * KeyguardBouncer.EXPANSION_VISIBLE),
+ eq(SCREEN_HEIGHT_PX * KeyguardBouncerConstants.EXPANSION_VISIBLE),
eq(velocityY), eq((float) SCREEN_HEIGHT_PX));
verify(mValueAnimator).start();
verify(mUiEventLogger, never()).log(any());
@@ -381,12 +383,12 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
swipeToPosition(swipeDownPercentage, Direction.DOWN, velocityY);
verify(mValueAnimatorCreator).create(eq(swipeDownPercentage),
- eq(KeyguardBouncer.EXPANSION_HIDDEN));
+ eq(KeyguardBouncerConstants.EXPANSION_HIDDEN));
verify(mValueAnimator, never()).addListener(any());
verify(mFlingAnimationUtilsClosing).apply(eq(mValueAnimator),
eq(SCREEN_HEIGHT_PX * swipeDownPercentage),
- eq(SCREEN_HEIGHT_PX * KeyguardBouncer.EXPANSION_HIDDEN),
+ eq(SCREEN_HEIGHT_PX * KeyguardBouncerConstants.EXPANSION_HIDDEN),
eq(velocityY), eq((float) SCREEN_HEIGHT_PX));
verify(mValueAnimator).start();
verify(mUiEventLogger, never()).log(any());
@@ -405,7 +407,8 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
final float velocityY = -1;
swipeToPosition(swipeUpPercentage, Direction.UP, velocityY);
- verify(mValueAnimatorCreator).create(eq(expansion), eq(KeyguardBouncer.EXPANSION_VISIBLE));
+ verify(mValueAnimatorCreator).create(eq(expansion),
+ eq(KeyguardBouncerConstants.EXPANSION_VISIBLE));
ArgumentCaptor<AnimatorListenerAdapter> endAnimationListenerCaptor =
ArgumentCaptor.forClass(AnimatorListenerAdapter.class);
@@ -413,7 +416,7 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
AnimatorListenerAdapter endAnimationListener = endAnimationListenerCaptor.getValue();
verify(mFlingAnimationUtils).apply(eq(mValueAnimator), eq(SCREEN_HEIGHT_PX * expansion),
- eq(SCREEN_HEIGHT_PX * KeyguardBouncer.EXPANSION_VISIBLE),
+ eq(SCREEN_HEIGHT_PX * KeyguardBouncerConstants.EXPANSION_VISIBLE),
eq(velocityY), eq((float) SCREEN_HEIGHT_PX));
verify(mValueAnimator).start();
verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_SWIPED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 122d7fdbfa47..f55b86686152 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -485,6 +486,38 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
assertTrue(mViewMediator.isShowingAndNotOccluded());
}
+ @Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ public void testDoKeyguardWhileInteractive_resets() {
+ mViewMediator.setShowingLocked(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ TestableLooper.get(this).processAllMessages();
+
+ when(mPowerManager.isInteractive()).thenReturn(true);
+
+ mViewMediator.onSystemReady();
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue(mViewMediator.isShowingAndNotOccluded());
+ verify(mStatusBarKeyguardViewManager).reset(anyBoolean());
+ }
+
+ @Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ public void testDoKeyguardWhileNotInteractive_showsInsteadOfResetting() {
+ mViewMediator.setShowingLocked(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ TestableLooper.get(this).processAllMessages();
+
+ when(mPowerManager.isInteractive()).thenReturn(false);
+
+ mViewMediator.onSystemReady();
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue(mViewMediator.isShowingAndNotOccluded());
+ verify(mStatusBarKeyguardViewManager, never()).reset(anyBoolean());
+ }
+
private void createAndStartViewMediator() {
mViewMediator = new KeyguardViewMediator(
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index 7c10108d5b45..15b85ded5fd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -38,6 +38,7 @@ import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestDispatcher
@@ -83,169 +84,205 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
settings = FakeSettings()
- underTest = DoNotDisturbQuickAffordanceConfig(
- context,
- zenModeController,
- settings,
- userTracker,
- testDispatcher,
- conditionUri,
- enableZenModeDialog,
- )
+ underTest =
+ DoNotDisturbQuickAffordanceConfig(
+ context,
+ zenModeController,
+ settings,
+ userTracker,
+ testDispatcher,
+ conditionUri,
+ enableZenModeDialog,
+ )
}
@Test
- fun `dnd not available - picker state hidden`() = testScope.runTest {
- //given
- whenever(zenModeController.isZenAvailable).thenReturn(false)
+ fun `dnd not available - picker state hidden`() =
+ testScope.runTest {
+ // given
+ whenever(zenModeController.isZenAvailable).thenReturn(false)
- //when
- val result = underTest.getPickerScreenState()
+ // when
+ val result = underTest.getPickerScreenState()
- //then
- assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice, result)
- }
+ // then
+ assertEquals(
+ KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice,
+ result
+ )
+ }
@Test
- fun `dnd available - picker state visible`() = testScope.runTest {
- //given
- whenever(zenModeController.isZenAvailable).thenReturn(true)
-
- //when
- val result = underTest.getPickerScreenState()
-
- //then
- assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.Default, result)
- }
+ fun `dnd available - picker state visible`() =
+ testScope.runTest {
+ // given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+
+ // when
+ val result = underTest.getPickerScreenState()
+
+ // then
+ assertThat(result)
+ .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
+ val defaultPickerState =
+ result as KeyguardQuickAffordanceConfig.PickerScreenState.Default
+ assertThat(defaultPickerState.configureIntent).isNotNull()
+ assertThat(defaultPickerState.configureIntent?.action)
+ .isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS)
+ }
@Test
- fun `onTriggered - dnd mode is not ZEN_MODE_OFF - set to ZEN_MODE_OFF`() = testScope.runTest {
- //given
- whenever(zenModeController.isZenAvailable).thenReturn(true)
- whenever(zenModeController.zen).thenReturn(-1)
- settings.putInt(Settings.Secure.ZEN_DURATION, -2)
- collectLastValue(underTest.lockScreenState)
- runCurrent()
-
- //when
- val result = underTest.onTriggered(null)
- verify(zenModeController).setZen(spyZenMode.capture(), spyConditionId.capture(), eq(DoNotDisturbQuickAffordanceConfig.TAG))
-
- //then
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
- assertEquals(ZEN_MODE_OFF, spyZenMode.value)
- assertNull(spyConditionId.value)
- }
+ fun `onTriggered - dnd mode is not ZEN_MODE_OFF - set to ZEN_MODE_OFF`() =
+ testScope.runTest {
+ // given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(-1)
+ settings.putInt(Settings.Secure.ZEN_DURATION, -2)
+ collectLastValue(underTest.lockScreenState)
+ runCurrent()
+
+ // when
+ val result = underTest.onTriggered(null)
+ verify(zenModeController)
+ .setZen(
+ spyZenMode.capture(),
+ spyConditionId.capture(),
+ eq(DoNotDisturbQuickAffordanceConfig.TAG)
+ )
+
+ // then
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(ZEN_MODE_OFF, spyZenMode.value)
+ assertNull(spyConditionId.value)
+ }
@Test
- fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting is FOREVER - set zen with no condition`() = testScope.runTest {
- //given
- whenever(zenModeController.isZenAvailable).thenReturn(true)
- whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
- settings.putInt(Settings.Secure.ZEN_DURATION, ZEN_DURATION_FOREVER)
- collectLastValue(underTest.lockScreenState)
- runCurrent()
-
- //when
- val result = underTest.onTriggered(null)
- verify(zenModeController).setZen(spyZenMode.capture(), spyConditionId.capture(), eq(DoNotDisturbQuickAffordanceConfig.TAG))
-
- //then
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
- assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, spyZenMode.value)
- assertNull(spyConditionId.value)
- }
+ fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting FOREVER - set zen without condition`() =
+ testScope.runTest {
+ // given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+ settings.putInt(Settings.Secure.ZEN_DURATION, ZEN_DURATION_FOREVER)
+ collectLastValue(underTest.lockScreenState)
+ runCurrent()
+
+ // when
+ val result = underTest.onTriggered(null)
+ verify(zenModeController)
+ .setZen(
+ spyZenMode.capture(),
+ spyConditionId.capture(),
+ eq(DoNotDisturbQuickAffordanceConfig.TAG)
+ )
+
+ // then
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, spyZenMode.value)
+ assertNull(spyConditionId.value)
+ }
@Test
- fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting is not FOREVER or PROMPT - set zen with condition`() = testScope.runTest {
- //given
- whenever(zenModeController.isZenAvailable).thenReturn(true)
- whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
- settings.putInt(Settings.Secure.ZEN_DURATION, -900)
- collectLastValue(underTest.lockScreenState)
- runCurrent()
-
- //when
- val result = underTest.onTriggered(null)
- verify(zenModeController).setZen(spyZenMode.capture(), spyConditionId.capture(), eq(DoNotDisturbQuickAffordanceConfig.TAG))
-
- //then
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
- assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, spyZenMode.value)
- assertEquals(conditionUri, spyConditionId.value)
- }
+ fun `onTriggered - dnd ZEN_MODE_OFF - setting not FOREVER or PROMPT - zen with condition`() =
+ testScope.runTest {
+ // given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+ settings.putInt(Settings.Secure.ZEN_DURATION, -900)
+ collectLastValue(underTest.lockScreenState)
+ runCurrent()
+
+ // when
+ val result = underTest.onTriggered(null)
+ verify(zenModeController)
+ .setZen(
+ spyZenMode.capture(),
+ spyConditionId.capture(),
+ eq(DoNotDisturbQuickAffordanceConfig.TAG)
+ )
+
+ // then
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, spyZenMode.value)
+ assertEquals(conditionUri, spyConditionId.value)
+ }
@Test
- fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting is PROMPT - show dialog`() = testScope.runTest {
- //given
- val expandable: Expandable = mock()
- whenever(zenModeController.isZenAvailable).thenReturn(true)
- whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
- settings.putInt(Settings.Secure.ZEN_DURATION, ZEN_DURATION_PROMPT)
- whenever(enableZenModeDialog.createDialog()).thenReturn(mock())
- collectLastValue(underTest.lockScreenState)
- runCurrent()
-
- //when
- val result = underTest.onTriggered(expandable)
-
- //then
- assertTrue(result is KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog)
- assertEquals(expandable, (result as KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog).expandable)
- }
+ fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting is PROMPT - show dialog`() =
+ testScope.runTest {
+ // given
+ val expandable: Expandable = mock()
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+ settings.putInt(Settings.Secure.ZEN_DURATION, ZEN_DURATION_PROMPT)
+ whenever(enableZenModeDialog.createDialog()).thenReturn(mock())
+ collectLastValue(underTest.lockScreenState)
+ runCurrent()
+
+ // when
+ val result = underTest.onTriggered(expandable)
+
+ // then
+ assertTrue(result is KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog)
+ assertEquals(
+ expandable,
+ (result as KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog).expandable
+ )
+ }
@Test
- fun `lockScreenState - dndAvailable starts as true - changes to false - State moves to Hidden`() = testScope.runTest {
- //given
- whenever(zenModeController.isZenAvailable).thenReturn(true)
- val callbackCaptor: ArgumentCaptor<ZenModeController.Callback> = argumentCaptor()
- val valueSnapshot = collectLastValue(underTest.lockScreenState)
- val secondLastValue = valueSnapshot()
- verify(zenModeController).addCallback(callbackCaptor.capture())
-
- //when
- callbackCaptor.value.onZenAvailableChanged(false)
- val lastValue = valueSnapshot()
-
- //then
- assertTrue(secondLastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
- assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
- }
+ fun `lockScreenState - dndAvailable starts as true - change to false - State is Hidden`() =
+ testScope.runTest {
+ // given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ val callbackCaptor: ArgumentCaptor<ZenModeController.Callback> = argumentCaptor()
+ val valueSnapshot = collectLastValue(underTest.lockScreenState)
+ val secondLastValue = valueSnapshot()
+ verify(zenModeController).addCallback(callbackCaptor.capture())
+
+ // when
+ callbackCaptor.value.onZenAvailableChanged(false)
+ val lastValue = valueSnapshot()
+
+ // then
+ assertTrue(secondLastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
+ assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ }
@Test
- fun `lockScreenState - dndMode starts as ZEN_MODE_OFF - changes to not OFF - State moves to Visible`() = testScope.runTest {
- //given
- whenever(zenModeController.isZenAvailable).thenReturn(true)
- whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
- val valueSnapshot = collectLastValue(underTest.lockScreenState)
- val secondLastValue = valueSnapshot()
- val callbackCaptor: ArgumentCaptor<ZenModeController.Callback> = argumentCaptor()
- verify(zenModeController).addCallback(callbackCaptor.capture())
-
- //when
- callbackCaptor.value.onZenChanged(ZEN_MODE_IMPORTANT_INTERRUPTIONS)
- val lastValue = valueSnapshot()
-
- //then
- assertEquals(
- KeyguardQuickAffordanceConfig.LockScreenState.Visible(
- Icon.Resource(
- R.drawable.qs_dnd_icon_off,
- ContentDescription.Resource(R.string.dnd_is_off)
+ fun `lockScreenState - dndMode starts as ZEN_MODE_OFF - change to not OFF - State Visible`() =
+ testScope.runTest {
+ // given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+ val valueSnapshot = collectLastValue(underTest.lockScreenState)
+ val secondLastValue = valueSnapshot()
+ val callbackCaptor: ArgumentCaptor<ZenModeController.Callback> = argumentCaptor()
+ verify(zenModeController).addCallback(callbackCaptor.capture())
+
+ // when
+ callbackCaptor.value.onZenChanged(ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ val lastValue = valueSnapshot()
+
+ // then
+ assertEquals(
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ Icon.Resource(
+ R.drawable.qs_dnd_icon_off,
+ ContentDescription.Resource(R.string.dnd_is_off)
+ ),
+ ActivationState.Inactive
),
- ActivationState.Inactive
- ),
- secondLastValue,
- )
- assertEquals(
- KeyguardQuickAffordanceConfig.LockScreenState.Visible(
- Icon.Resource(
- R.drawable.qs_dnd_icon_on,
- ContentDescription.Resource(R.string.dnd_is_on)
+ secondLastValue,
+ )
+ assertEquals(
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ Icon.Resource(
+ R.drawable.qs_dnd_icon_on,
+ ContentDescription.Resource(R.string.dnd_is_on)
+ ),
+ ActivationState.Active
),
- ActivationState.Active
- ),
- lastValue,
- )
- }
-} \ No newline at end of file
+ lastValue,
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
index 6255980601ac..9d2ddffddb5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
@@ -141,7 +141,7 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
whenever(controller.isAbleToOpenCameraApp).thenReturn(true)
assertThat(underTest.getPickerScreenState())
- .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default)
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index d875dd94da3e..ca44fa18f6c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -159,7 +159,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
setUpState()
assertThat(underTest.getPickerScreenState())
- .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default)
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index f8f2a56d4808..32cec09c3580 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -168,6 +168,25 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
assertThat(wtfHandler.failed).isTrue()
}
+ @Test
+ fun `Attempt to manually update transition after CANCELED state throws exception`() {
+ val uuid =
+ underTest.startTransition(
+ TransitionInfo(
+ ownerName = OWNER_NAME,
+ from = AOD,
+ to = LOCKSCREEN,
+ animator = null,
+ )
+ )
+
+ checkNotNull(uuid).let {
+ underTest.updateTransition(it, 0.2f, TransitionState.CANCELED)
+ underTest.updateTransition(it, 0.5f, TransitionState.RUNNING)
+ }
+ assertThat(wtfHandler.failed).isTrue()
+ }
+
private fun listWithStep(
step: BigDecimal,
start: BigDecimal = BigDecimal.ZERO,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index b3cee2273012..5a7a3d49b628 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
+import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators
@@ -38,6 +39,7 @@ import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -55,6 +57,7 @@ import org.mockito.MockitoAnnotations
*/
@SmallTest
@RunWith(JUnit4::class)
+@FlakyTest(bugId = 265303901)
class KeyguardTransitionScenariosTest : SysuiTestCase() {
private lateinit var testScope: TestScope
@@ -177,7 +180,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
keyguardRepository.setDreamingWithOverlay(false)
// AND occluded has stopped
keyguardRepository.setKeyguardOccluded(false)
- runCurrent()
+ advanceUntilIdle()
val info =
withArgCaptor<TransitionInfo> {
@@ -506,7 +509,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
- // THEN a transition to DOZING should occur
+ // THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.GONE)
assertThat(info.to).isEqualTo(KeyguardState.AOD)
@@ -515,6 +518,49 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
coroutineContext.cancelChildren()
}
+ @Test
+ fun `GONE to DREAMING`() =
+ testScope.runTest {
+ // GIVEN a device that is not dreaming or dozing
+ keyguardRepository.setDreamingWithOverlay(false)
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+ )
+ runCurrent()
+
+ // GIVEN a prior transition has run to GONE
+ runner.startTransition(
+ testScope,
+ TransitionInfo(
+ ownerName = "",
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ animator =
+ ValueAnimator().apply {
+ duration = 10
+ interpolator = Interpolators.LINEAR
+ },
+ )
+ )
+ reset(mockTransitionRepository)
+
+ // WHEN the device begins to dream
+ keyguardRepository.setDreamingWithOverlay(true)
+ advanceUntilIdle()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(mockTransitionRepository).startTransition(capture())
+ }
+ // THEN a transition to DREAMING should occur
+ assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.GONE)
+ assertThat(info.to).isEqualTo(KeyguardState.DREAMING)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
private fun startingToWake() =
WakefulnessModel(
WakefulnessState.STARTING_TO_WAKE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
index db9c4e713e37..fbfeca9c2a25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
@@ -19,7 +19,6 @@ package com.android.systemui.keyguard.domain.interactor
import android.view.View
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.phone.KeyguardBouncer
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,8 +33,10 @@ class PrimaryBouncerCallbackInteractorTest : SysuiTestCase() {
private val mPrimaryBouncerCallbackInteractor = PrimaryBouncerCallbackInteractor()
@Mock
private lateinit var mPrimaryBouncerExpansionCallback:
- KeyguardBouncer.PrimaryBouncerExpansionCallback
- @Mock private lateinit var keyguardResetCallback: KeyguardBouncer.KeyguardResetCallback
+ PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
+ @Mock
+ private lateinit var keyguardResetCallback:
+ PrimaryBouncerCallbackInteractor.KeyguardResetCallback
@Before
fun setup() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
index a6fc13bcb011..7f48ea19c91a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -30,11 +30,11 @@ import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.BouncerViewDelegate
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN
-import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.any
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
new file mode 100644
index 000000000000..7fa204bb980b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.AnimationParams
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel.Companion.LOCKSCREEN_ALPHA
+import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class GoneToDreamingTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: GoneToDreamingTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ val interactor = KeyguardTransitionInteractor(repository)
+ underTest = GoneToDreamingTransitionViewModel(interactor)
+ }
+
+ @Test
+ fun lockscreenFadeOut() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+
+ val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
+
+ // Should start running here...
+ repository.sendTransitionStep(step(0f))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.2f))
+ // ...up to here
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(1f))
+
+ // Only three values should be present, since the dream overlay runs for a small
+ // fraction
+ // of the overall animation time
+ assertThat(values.size).isEqualTo(3)
+ assertThat(values[0]).isEqualTo(1f - animValue(0f, LOCKSCREEN_ALPHA))
+ assertThat(values[1]).isEqualTo(1f - animValue(0.1f, LOCKSCREEN_ALPHA))
+ assertThat(values[2]).isEqualTo(1f - animValue(0.2f, LOCKSCREEN_ALPHA))
+
+ job.cancel()
+ }
+
+ @Test
+ fun lockscreenTranslationY() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+
+ val pixels = 100
+ val job =
+ underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
+
+ // Should start running here...
+ repository.sendTransitionStep(step(0f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.5f))
+ // ...up to here
+ repository.sendTransitionStep(step(1f))
+ // And a final reset event on CANCEL
+ repository.sendTransitionStep(step(0.8f, TransitionState.CANCELED))
+
+ assertThat(values.size).isEqualTo(4)
+ assertThat(values[0])
+ .isEqualTo(
+ EMPHASIZED_ACCELERATE.getInterpolation(
+ animValue(0f, LOCKSCREEN_TRANSLATION_Y)
+ ) * pixels
+ )
+ assertThat(values[1])
+ .isEqualTo(
+ EMPHASIZED_ACCELERATE.getInterpolation(
+ animValue(0.3f, LOCKSCREEN_TRANSLATION_Y)
+ ) * pixels
+ )
+ assertThat(values[2])
+ .isEqualTo(
+ EMPHASIZED_ACCELERATE.getInterpolation(
+ animValue(0.5f, LOCKSCREEN_TRANSLATION_Y)
+ ) * pixels
+ )
+ assertThat(values[3]).isEqualTo(0f)
+ job.cancel()
+ }
+
+ private fun animValue(stepValue: Float, params: AnimationParams): Float {
+ val totalDuration = TO_DREAMING_DURATION
+ val startValue = (params.startTime / totalDuration).toFloat()
+
+ val multiplier = (totalDuration / params.duration).toFloat()
+ return (stepValue - startValue) * multiplier
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.DREAMING,
+ value = value,
+ transitionState = state,
+ ownerName = "GoneToDreamingTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index 739059126b04..539fc2c1548e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -67,8 +67,7 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
repository.sendTransitionStep(step(1f))
// Only three values should be present, since the dream overlay runs for a small
- // fraction
- // of the overall animation time
+ // fraction of the overall animation time
assertThat(values.size).isEqualTo(3)
assertThat(values[0]).isEqualTo(1f - animValue(0f, LOCKSCREEN_ALPHA))
assertThat(values[1]).isEqualTo(1f - animValue(0.1f, LOCKSCREEN_ALPHA))
@@ -92,8 +91,10 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
repository.sendTransitionStep(step(0.5f))
// ...up to here
repository.sendTransitionStep(step(1f))
+ // And a final reset event on FINISHED
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
- assertThat(values.size).isEqualTo(3)
+ assertThat(values.size).isEqualTo(4)
assertThat(values[0])
.isEqualTo(
EMPHASIZED_ACCELERATE.getInterpolation(
@@ -112,6 +113,8 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
animValue(0.5f, LOCKSCREEN_TRANSLATION_Y)
) * pixels
)
+ assertThat(values[3]).isEqualTo(0f)
+
job.cancel()
}
@@ -123,12 +126,15 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
return (stepValue - startValue) * multiplier
}
- private fun step(value: Float): TransitionStep {
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
return TransitionStep(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.DREAMING,
value = value,
- transitionState = TransitionState.RUNNING,
+ transitionState = state,
ownerName = "LockscreenToDreamingTransitionViewModelTest"
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java
index 4d2d0f05b76a..c0639f34484c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java
@@ -79,7 +79,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
USER_ID, true, APP, null, ARTIST, TITLE, null,
new ArrayList<>(), new ArrayList<>(), null, PACKAGE, null, null, null, true, null,
MediaData.PLAYBACK_LOCAL, false, KEY, false, false, false, 0L,
- InstanceId.fakeInstanceId(-1), -1);
+ InstanceId.fakeInstanceId(-1), -1, false);
mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME, null, false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index 52b694fac07c..1687fdc9f76c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.media.controls.pipeline
import android.app.Notification
+import android.app.Notification.FLAG_NO_CLEAR
import android.app.Notification.MediaStyle
import android.app.PendingIntent
import android.app.smartspace.SmartspaceAction
@@ -228,6 +229,7 @@ class MediaDataManagerTest : SysuiTestCase() {
whenever(mediaSmartspaceTarget.iconGrid).thenReturn(validRecommendationList)
whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(1234L)
whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(false)
+ whenever(mediaFlags.isExplicitIndicatorEnabled()).thenReturn(true)
whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId())
}
@@ -300,6 +302,60 @@ class MediaDataManagerTest : SysuiTestCase() {
}
@Test
+ fun testLoadMetadata_withExplicitIndicator() {
+ val metadata =
+ MediaMetadata.Builder().run {
+ putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST)
+ putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
+ putLong(
+ MediaConstants.METADATA_KEY_IS_EXPLICIT,
+ MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT
+ )
+ build()
+ }
+ whenever(mediaControllerFactory.create(anyObject())).thenReturn(controller)
+ whenever(controller.metadata).thenReturn(metadata)
+
+ mediaDataManager.addListener(listener)
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(KEY),
+ eq(null),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ assertThat(mediaDataCaptor.value!!.isExplicit).isTrue()
+ }
+
+ @Test
+ fun testOnMetaDataLoaded_withoutExplicitIndicator() {
+ whenever(mediaControllerFactory.create(anyObject())).thenReturn(controller)
+ whenever(controller.metadata).thenReturn(metadataBuilder.build())
+
+ mediaDataManager.addListener(listener)
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(KEY),
+ eq(null),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ assertThat(mediaDataCaptor.value!!.isExplicit).isFalse()
+ }
+
+ @Test
fun testOnMetaDataLoaded_callsListener() {
addNotificationAndLoad()
verify(logger)
@@ -603,6 +659,53 @@ class MediaDataManagerTest : SysuiTestCase() {
}
@Test
+ fun testAddResumptionControls_withExplicitIndicator() {
+ val bundle = Bundle()
+ // WHEN resumption controls are added with explicit indicator
+ bundle.putLong(
+ MediaConstants.METADATA_KEY_IS_EXPLICIT,
+ MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT
+ )
+ val desc =
+ MediaDescription.Builder().run {
+ setTitle(SESSION_TITLE)
+ setExtras(bundle)
+ build()
+ }
+ val currentTime = clock.elapsedRealtime()
+ mediaDataManager.addResumptionControls(
+ USER_ID,
+ desc,
+ Runnable {},
+ session.sessionToken,
+ APP_NAME,
+ pendingIntent,
+ PACKAGE_NAME
+ )
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+ // THEN the media data indicates that it is for resumption
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(PACKAGE_NAME),
+ eq(null),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ val data = mediaDataCaptor.value
+ assertThat(data.resumption).isTrue()
+ assertThat(data.song).isEqualTo(SESSION_TITLE)
+ assertThat(data.app).isEqualTo(APP_NAME)
+ assertThat(data.actions).hasSize(1)
+ assertThat(data.semanticActions!!.playOrPause).isNotNull()
+ assertThat(data.lastActive).isAtLeast(currentTime)
+ assertThat(data.isExplicit).isTrue()
+ verify(logger).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
+ }
+
+ @Test
fun testResumptionDisabled_dismissesResumeControls() {
// WHEN there are resume controls and resumption is switched off
val desc =
@@ -1349,6 +1452,39 @@ class MediaDataManagerTest : SysuiTestCase() {
assertThat(mediaDataCaptor.value.semanticActions).isNull()
}
+ @Test
+ fun testNoClearNotOngoing_canDismiss() {
+ mediaNotification =
+ SbnBuilder().run {
+ setPkg(PACKAGE_NAME)
+ modifyNotification(context).also {
+ it.setSmallIcon(android.R.drawable.ic_media_pause)
+ it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+ it.setOngoing(false)
+ it.setFlag(FLAG_NO_CLEAR, true)
+ }
+ build()
+ }
+ addNotificationAndLoad()
+ assertThat(mediaDataCaptor.value.isClearable).isTrue()
+ }
+
+ @Test
+ fun testOngoing_cannotDismiss() {
+ mediaNotification =
+ SbnBuilder().run {
+ setPkg(PACKAGE_NAME)
+ modifyNotification(context).also {
+ it.setSmallIcon(android.R.drawable.ic_media_pause)
+ it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+ it.setOngoing(true)
+ }
+ build()
+ }
+ addNotificationAndLoad()
+ assertThat(mediaDataCaptor.value.isClearable).isFalse()
+ }
+
/** Helper function to add a media notification and capture the resulting MediaData */
private fun addNotificationAndLoad() {
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index b65f5cb51aaf..b35dd266e422 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -54,6 +54,7 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.LiveData
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
+import com.android.internal.widget.CachingIconView
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
@@ -81,6 +82,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.surfaceeffects.ripple.MultiRippleView
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.concurrency.FakeExecutor
@@ -154,6 +156,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Mock private lateinit var albumView: ImageView
private lateinit var titleText: TextView
private lateinit var artistText: TextView
+ private lateinit var explicitIndicator: CachingIconView
private lateinit var seamless: ViewGroup
private lateinit var seamlessButton: View
@Mock private lateinit var seamlessBackground: RippleDrawable
@@ -216,14 +219,15 @@ public class MediaControlPanelTest : SysuiTestCase() {
this.set(Flags.UMO_SURFACE_RIPPLE, false)
this.set(Flags.UMO_TURBULENCE_NOISE, false)
this.set(Flags.MEDIA_FALSING_PENALTY, true)
+ this.set(Flags.MEDIA_EXPLICIT_INDICATOR, true)
}
@JvmField @Rule val mockito = MockitoJUnit.rule()
@Before
fun setUp() {
- bgExecutor = FakeExecutor(FakeSystemClock())
- mainExecutor = FakeExecutor(FakeSystemClock())
+ bgExecutor = FakeExecutor(clock)
+ mainExecutor = FakeExecutor(clock)
whenever(mediaViewController.expandedLayout).thenReturn(expandedSet)
whenever(mediaViewController.collapsedLayout).thenReturn(collapsedSet)
@@ -350,6 +354,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
appIcon = ImageView(context)
titleText = TextView(context)
artistText = TextView(context)
+ explicitIndicator = CachingIconView(context).also { it.id = R.id.media_explicit_indicator }
seamless = FrameLayout(context)
seamless.foreground = seamlessBackground
seamlessButton = View(context)
@@ -396,6 +401,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
whenever(albumView.foreground).thenReturn(mock(Drawable::class.java))
whenever(viewHolder.titleText).thenReturn(titleText)
whenever(viewHolder.artistText).thenReturn(artistText)
+ whenever(viewHolder.explicitIndicator).thenReturn(explicitIndicator)
whenever(seamlessBackground.getDrawable(0)).thenReturn(mock(GradientDrawable::class.java))
whenever(viewHolder.seamless).thenReturn(seamless)
whenever(viewHolder.seamlessButton).thenReturn(seamlessButton)
@@ -1019,6 +1025,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindText() {
+ useRealConstraintSets()
player.attachPlayer(viewHolder)
player.bindPlayer(mediaData, PACKAGE)
@@ -1036,6 +1043,8 @@ public class MediaControlPanelTest : SysuiTestCase() {
handler.onAnimationEnd(mockAnimator)
assertThat(titleText.getText()).isEqualTo(TITLE)
assertThat(artistText.getText()).isEqualTo(ARTIST)
+ assertThat(expandedSet.getVisibility(explicitIndicator.id)).isEqualTo(ConstraintSet.GONE)
+ assertThat(collapsedSet.getVisibility(explicitIndicator.id)).isEqualTo(ConstraintSet.GONE)
// Rebinding should not trigger animation
player.bindPlayer(mediaData, PACKAGE)
@@ -1043,6 +1052,36 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
+ fun bindTextWithExplicitIndicator() {
+ useRealConstraintSets()
+ val mediaDataWitExp = mediaData.copy(isExplicit = true)
+ player.attachPlayer(viewHolder)
+ player.bindPlayer(mediaDataWitExp, PACKAGE)
+
+ // Capture animation handler
+ val captor = argumentCaptor<Animator.AnimatorListener>()
+ verify(mockAnimator, times(2)).addListener(captor.capture())
+ val handler = captor.value
+
+ // Validate text views unchanged but animation started
+ assertThat(titleText.getText()).isEqualTo("")
+ assertThat(artistText.getText()).isEqualTo("")
+ verify(mockAnimator, times(1)).start()
+
+ // Binding only after animator runs
+ handler.onAnimationEnd(mockAnimator)
+ assertThat(titleText.getText()).isEqualTo(TITLE)
+ assertThat(artistText.getText()).isEqualTo(ARTIST)
+ assertThat(expandedSet.getVisibility(explicitIndicator.id)).isEqualTo(ConstraintSet.VISIBLE)
+ assertThat(collapsedSet.getVisibility(explicitIndicator.id))
+ .isEqualTo(ConstraintSet.VISIBLE)
+
+ // Rebinding should not trigger animation
+ player.bindPlayer(mediaData, PACKAGE)
+ verify(mockAnimator, times(3)).start()
+ }
+
+ @Test
fun bindTextInterrupted() {
val data0 = mediaData.copy(artist = "ARTIST_0")
val data1 = mediaData.copy(artist = "ARTIST_1")
@@ -2083,6 +2122,27 @@ public class MediaControlPanelTest : SysuiTestCase() {
assertThat(player.mRipplesFinishedListener).isNull()
}
+ @Test
+ fun playTurbulenceNoise_finishesAfterDuration() {
+ fakeFeatureFlag.set(Flags.UMO_SURFACE_RIPPLE, true)
+ fakeFeatureFlag.set(Flags.UMO_TURBULENCE_NOISE, true)
+
+ player.attachPlayer(viewHolder)
+
+ mainExecutor.execute {
+ player.mRipplesFinishedListener.onRipplesFinish()
+
+ assertThat(turbulenceNoiseView.visibility).isEqualTo(View.VISIBLE)
+
+ clock.advanceTime(
+ MediaControlPanel.TURBULENCE_NOISE_PLAY_DURATION +
+ TurbulenceNoiseAnimationConfig.DEFAULT_EASING_DURATION_IN_MILLIS.toLong()
+ )
+
+ assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
+ }
+ }
+
private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener =
withArgCaptor {
verify(seekBarViewModel).setScrubbingChangeListener(capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index 19d2d334b884..1042ea714936 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -1,12 +1,16 @@
package com.android.systemui.mediaprojection.appselector
import android.content.ComponentName
+import android.os.UserHandle
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import org.junit.Test
@@ -21,11 +25,17 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
private val scope = CoroutineScope(Dispatchers.Unconfined)
private val appSelectorComponentName = ComponentName("com.test", "AppSelector")
+ private val hostUserHandle = UserHandle.of(123)
+ private val otherUserHandle = UserHandle.of(456)
+
private val view: MediaProjectionAppSelectorView = mock()
+ private val featureFlags: FeatureFlags = mock()
private val controller = MediaProjectionAppSelectorController(
taskListProvider,
view,
+ featureFlags,
+ hostUserHandle,
scope,
appSelectorComponentName
)
@@ -98,15 +108,72 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
)
}
+ @Test
+ fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesDisabled_bindsOnlyTasksWithHostProfile() {
+ givenEnterprisePoliciesFeatureFlag(enabled = false)
+
+ val tasks = listOf(
+ createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
+ createRecentTask(taskId = 2, userId = otherUserHandle.identifier),
+ createRecentTask(taskId = 3, userId = hostUserHandle.identifier),
+ createRecentTask(taskId = 4, userId = otherUserHandle.identifier),
+ createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
+ )
+ taskListProvider.tasks = tasks
+
+ controller.init()
+
+ verify(view).bind(
+ listOf(
+ createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
+ createRecentTask(taskId = 3, userId = hostUserHandle.identifier),
+ createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
+ )
+ )
+ }
+
+ @Test
+ fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesEnabled_bindsAllTasks() {
+ givenEnterprisePoliciesFeatureFlag(enabled = true)
+
+ val tasks = listOf(
+ createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
+ createRecentTask(taskId = 2, userId = otherUserHandle.identifier),
+ createRecentTask(taskId = 3, userId = hostUserHandle.identifier),
+ createRecentTask(taskId = 4, userId = otherUserHandle.identifier),
+ createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
+ )
+ taskListProvider.tasks = tasks
+
+ controller.init()
+
+ // TODO(b/233348916) should filter depending on the policies
+ verify(view).bind(
+ listOf(
+ createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
+ createRecentTask(taskId = 2, userId = otherUserHandle.identifier),
+ createRecentTask(taskId = 3, userId = hostUserHandle.identifier),
+ createRecentTask(taskId = 4, userId = otherUserHandle.identifier),
+ createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
+ )
+ )
+ }
+
+ private fun givenEnterprisePoliciesFeatureFlag(enabled: Boolean) {
+ whenever(featureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
+ .thenReturn(enabled)
+ }
+
private fun createRecentTask(
taskId: Int,
- topActivityComponent: ComponentName? = null
+ topActivityComponent: ComponentName? = null,
+ userId: Int = hostUserHandle.identifier
): RecentTask {
return RecentTask(
taskId = taskId,
topActivityComponent = topActivityComponent,
baseIntentComponent = ComponentName("com", "Test"),
- userId = 0,
+ userId = userId,
colorBackground = 0
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 9bf27a26a682..8b0342eda633 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -51,6 +51,7 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.LightBarController;
@@ -102,10 +103,10 @@ public class NavigationBarControllerTest extends SysuiTestCase {
mock(NavBarHelper.class),
mTaskbarDelegate,
mNavigationBarFactory,
- mock(StatusBarKeyguardViewManager.class),
mock(DumpManager.class),
mock(AutoHideController.class),
mock(LightBarController.class),
+ TaskStackChangeListeners.getTestInstance(),
Optional.of(mock(Pip.class)),
Optional.of(mock(BackAnimation.class)),
mock(FeatureFlags.class)));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 80adbf025e0b..2ad865e6ef11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -28,6 +28,7 @@ import static android.view.WindowInsets.Type.ime;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -44,6 +45,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.display.DisplayManagerGlobal;
@@ -90,6 +92,7 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shared.rotation.RotationButtonController;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -203,6 +206,8 @@ public class NavigationBarTest extends SysuiTestCase {
private ViewRootImpl mViewRootImpl;
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
private DeviceConfigProxyFake mDeviceConfigProxyFake = new DeviceConfigProxyFake();
+ private TaskStackChangeListeners mTaskStackChangeListeners =
+ TaskStackChangeListeners.getTestInstance();
@Rule
public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
@@ -437,6 +442,14 @@ public class NavigationBarTest extends SysuiTestCase {
verify(mNavBarHelper, times(1)).getCurrentSysuiState();
}
+ @Test
+ public void testScreenPinningEnabled_updatesSysuiState() {
+ mNavigationBar.init();
+ mTaskStackChangeListeners.getListenerImpl().onLockTaskModeChanged(
+ ActivityManager.LOCK_TASK_MODE_PINNED);
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_SCREEN_PINNING), eq(true));
+ }
+
private NavigationBar createNavBar(Context context) {
DeviceProvisionedController deviceProvisionedController =
mock(DeviceProvisionedController.class);
@@ -481,7 +494,8 @@ public class NavigationBarTest extends SysuiTestCase {
mEdgeBackGestureHandler,
Optional.of(mock(BackAnimation.class)),
mUserContextProvider,
- mWakefulnessLifecycle));
+ mWakefulnessLifecycle,
+ mTaskStackChangeListeners));
}
private void processAllMessages() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
index 1742c6994246..537dfb821fef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
@@ -1,11 +1,14 @@
package com.android.systemui.navigationbar
+import android.app.ActivityManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.model.SysUiState
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler
import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.shared.system.QuickStepContract
+import com.android.systemui.shared.system.TaskStackChangeListeners
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.phone.AutoHideController
import com.android.systemui.statusbar.phone.LightBarController
@@ -14,6 +17,7 @@ import com.android.wm.shell.back.BackAnimation
import com.android.wm.shell.pip.Pip
import org.junit.Before
import org.junit.Test
+import org.mockito.ArgumentMatchers
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
@@ -30,6 +34,7 @@ class TaskbarDelegateTest : SysuiTestCase() {
val MODE_GESTURE = 0;
val MODE_THREE_BUTTON = 1;
+ private lateinit var mTaskStackChangeListeners: TaskStackChangeListeners
private lateinit var mTaskbarDelegate: TaskbarDelegate
@Mock
lateinit var mEdgeBackGestureHandlerFactory : EdgeBackGestureHandler.Factory
@@ -69,11 +74,12 @@ class TaskbarDelegateTest : SysuiTestCase() {
`when`(mLightBarControllerFactory.create(any())).thenReturn(mLightBarTransitionController)
`when`(mNavBarHelper.currentSysuiState).thenReturn(mCurrentSysUiState)
`when`(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState)
+ mTaskStackChangeListeners = TaskStackChangeListeners.getTestInstance()
mTaskbarDelegate = TaskbarDelegate(context, mEdgeBackGestureHandlerFactory,
mLightBarControllerFactory)
mTaskbarDelegate.setDependencies(mCommandQueue, mOverviewProxyService, mNavBarHelper,
mNavigationModeController, mSysUiState, mDumpManager, mAutoHideController,
- mLightBarController, mOptionalPip, mBackAnimation)
+ mLightBarController, mOptionalPip, mBackAnimation, mTaskStackChangeListeners)
}
@Test
@@ -90,4 +96,15 @@ class TaskbarDelegateTest : SysuiTestCase() {
mTaskbarDelegate.init(DISPLAY_ID)
verify(mEdgeBackGestureHandler, times(1)).onNavigationModeChanged(MODE_GESTURE)
}
+
+ @Test
+ fun screenPinningEnabled_updatesSysuiState() {
+ mTaskbarDelegate.init(DISPLAY_ID)
+ mTaskStackChangeListeners.listenerImpl.onLockTaskModeChanged(
+ ActivityManager.LOCK_TASK_MODE_PINNED)
+ verify(mSysUiState, times(1)).setFlag(
+ ArgumentMatchers.eq(QuickStepContract.SYSUI_STATE_SCREEN_PINNING),
+ ArgumentMatchers.eq(true)
+ )
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index fc90c1add5e9..8440455127bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -24,7 +24,7 @@ import android.os.UserManager
import android.test.suitebuilder.annotation.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
-import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.NOTES_ACTION
+import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.ACTION_CREATE_NOTE
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
@@ -50,7 +50,7 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidJUnit4::class)
internal class NoteTaskControllerTest : SysuiTestCase() {
- private val notesIntent = Intent(NOTES_ACTION)
+ private val notesIntent = Intent(ACTION_CREATE_NOTE)
@Mock lateinit var context: Context
@Mock lateinit var packageManager: PackageManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index 538131a4dd73..010ac5bbb2d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -106,7 +106,9 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
// region handleSystemKey
@Test
fun handleSystemKey_receiveValidSystemKey_shouldShowNoteTask() {
- createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
+ createNoteTaskInitializer()
+ .callbacks
+ .handleSystemKey(NoteTaskController.NOTE_TASK_KEY_EVENT)
verify(noteTaskController).showNoteTask()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskIntentResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskIntentResolverTest.kt
index dd2cc2ffc9db..bbe60f4ba493 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskIntentResolverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskIntentResolverTest.kt
@@ -23,11 +23,10 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ResolveInfoFlags
import android.content.pm.ResolveInfo
-import android.content.pm.ServiceInfo
import android.test.suitebuilder.annotation.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
-import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.NOTES_ACTION
+import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.ACTION_CREATE_NOTE
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -58,19 +57,13 @@ internal class NoteTaskIntentResolverTest : SysuiTestCase() {
}
private fun createResolveInfo(
- packageName: String = "PackageName",
- activityInfo: ActivityInfo? = null,
+ activityInfo: ActivityInfo? = createActivityInfo(),
): ResolveInfo {
- return ResolveInfo().apply {
- serviceInfo =
- ServiceInfo().apply {
- applicationInfo = ApplicationInfo().apply { this.packageName = packageName }
- }
- this.activityInfo = activityInfo
- }
+ return ResolveInfo().apply { this.activityInfo = activityInfo }
}
private fun createActivityInfo(
+ packageName: String = "PackageName",
name: String? = "ActivityName",
exported: Boolean = true,
enabled: Boolean = true,
@@ -87,6 +80,7 @@ internal class NoteTaskIntentResolverTest : SysuiTestCase() {
if (turnScreenOn) {
flags = flags or ActivityInfo.FLAG_TURN_SCREEN_ON
}
+ this.applicationInfo = ApplicationInfo().apply { this.packageName = packageName }
}
}
@@ -107,7 +101,8 @@ internal class NoteTaskIntentResolverTest : SysuiTestCase() {
val actual = resolver.resolveIntent()
val expected =
- Intent(NOTES_ACTION)
+ Intent(ACTION_CREATE_NOTE)
+ .setPackage("PackageName")
.setComponent(ComponentName("PackageName", "ActivityName"))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
// Compares the string representation of both intents, as they are different instances.
@@ -204,7 +199,9 @@ internal class NoteTaskIntentResolverTest : SysuiTestCase() {
@Test
fun resolveIntent_packageNameIsBlank_shouldReturnNull() {
- givenQueryIntentActivities { listOf(createResolveInfo(packageName = "")) }
+ givenQueryIntentActivities {
+ listOf(createResolveInfo(createActivityInfo(packageName = "")))
+ }
val actual = resolver.resolveIntent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
index f802a5e09228..ed9baf5b1c9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
@@ -109,11 +109,12 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() {
@Test
fun testEdgeElementsAlignedWithEdge_largeScreen() {
with(largeScreenConstraint) {
- assertThat(getConstraint(R.id.clock).layout.startToStart).isEqualTo(PARENT_ID)
- assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f)
+ assertThat(getConstraint(R.id.clock).layout.startToEnd).isEqualTo(R.id.begin_guide)
+ assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0.5f)
- assertThat(getConstraint(R.id.privacy_container).layout.endToEnd).isEqualTo(PARENT_ID)
- assertThat(getConstraint(R.id.privacy_container).layout.horizontalBias).isEqualTo(1f)
+ assertThat(getConstraint(R.id.privacy_container).layout.endToStart)
+ .isEqualTo(R.id.end_guide)
+ assertThat(getConstraint(R.id.privacy_container).layout.horizontalBias).isEqualTo(0.5f)
}
}
@@ -219,7 +220,12 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() {
.isEqualTo(cutoutEnd - padding)
}
- assertThat(changes.largeScreenConstraintsChanges).isNull()
+ with(largeScreenConstraint) {
+ assertThat(getConstraint(R.id.begin_guide).layout.guideBegin)
+ .isEqualTo(cutoutStart - padding)
+ assertThat(getConstraint(R.id.end_guide).layout.guideEnd)
+ .isEqualTo(cutoutEnd - padding)
+ }
}
@Test
@@ -246,7 +252,10 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() {
assertThat(getConstraint(R.id.end_guide).layout.guideEnd).isEqualTo(0)
}
- assertThat(changes.largeScreenConstraintsChanges).isNull()
+ with(largeScreenConstraint) {
+ assertThat(getConstraint(R.id.begin_guide).layout.guideBegin).isEqualTo(0)
+ assertThat(getConstraint(R.id.end_guide).layout.guideEnd).isEqualTo(0)
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index d0b42ae8ec56..0f3d4a8ca59a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -107,6 +107,7 @@ import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteracto
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
@@ -298,6 +299,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
@Mock private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
@Mock private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
@Mock private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
+ @Mock private GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Mock private CoroutineDispatcher mMainDispatcher;
@@ -522,6 +524,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mDreamingToLockscreenTransitionViewModel,
mOccludedToLockscreenTransitionViewModel,
mLockscreenToDreamingTransitionViewModel,
+ mGoneToDreamingTransitionViewModel,
mLockscreenToOccludedTransitionViewModel,
mMainDispatcher,
mKeyguardTransitionInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 08a9c9664ae0..526dc8d150fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -46,11 +46,14 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -68,6 +71,8 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
+import java.util.List;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
@@ -91,13 +96,21 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
@Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
@Mock private ShadeWindowLogger mShadeWindowLogger;
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
+ @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
-
+ private float mPreferredRefreshRate = -1;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ // Preferred refresh rate is equal to the first displayMode's refresh rate
+ mPreferredRefreshRate = mContext.getDisplay().getSupportedModes()[0].getRefreshRate();
+ overrideResource(
+ R.integer.config_keyguardRefreshRate,
+ (int) mPreferredRefreshRate
+ );
+
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
@@ -117,6 +130,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
mNotificationShadeWindowController.attach();
verify(mWindowManager).addView(eq(mNotificationShadeWindowView), any());
+ verify(mStatusBarStateController).addCallback(mStateListener.capture(), anyInt());
}
@Test
@@ -334,4 +348,59 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
assertThat(mLayoutParameters.getValue().screenOrientation)
.isEqualTo(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
}
+
+ @Test
+ public void udfpsEnrolled_minAndMaxRefreshRateSetToPreferredRefreshRate() {
+ // GIVEN udfps is enrolled
+ when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true);
+
+ // WHEN keyguard is showing
+ setKeyguardShowing();
+
+ // THEN min and max refresh rate is set to the preferredRefreshRate
+ verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), mLayoutParameters.capture());
+ final List<WindowManager.LayoutParams> lpList = mLayoutParameters.getAllValues();
+ final WindowManager.LayoutParams lp = lpList.get(lpList.size() - 1);
+ assertThat(lp.preferredMaxDisplayRefreshRate).isEqualTo(mPreferredRefreshRate);
+ assertThat(lp.preferredMinDisplayRefreshRate).isEqualTo(mPreferredRefreshRate);
+ }
+
+ @Test
+ public void udfpsNotEnrolled_refreshRateUnset() {
+ // GIVEN udfps is NOT enrolled
+ when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
+
+ // WHEN keyguard is showing
+ setKeyguardShowing();
+
+ // THEN min and max refresh rate aren't set (set to 0)
+ verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), mLayoutParameters.capture());
+ final List<WindowManager.LayoutParams> lpList = mLayoutParameters.getAllValues();
+ final WindowManager.LayoutParams lp = lpList.get(lpList.size() - 1);
+ assertThat(lp.preferredMaxDisplayRefreshRate).isEqualTo(0);
+ assertThat(lp.preferredMinDisplayRefreshRate).isEqualTo(0);
+ }
+
+ @Test
+ public void keyguardNotShowing_refreshRateUnset() {
+ // GIVEN UDFPS is enrolled
+ when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true);
+
+ // WHEN keyguard is NOT showing
+ mNotificationShadeWindowController.setKeyguardShowing(false);
+
+ // THEN min and max refresh rate aren't set (set to 0)
+ verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), mLayoutParameters.capture());
+ final List<WindowManager.LayoutParams> lpList = mLayoutParameters.getAllValues();
+ final WindowManager.LayoutParams lp = lpList.get(lpList.size() - 1);
+ assertThat(lp.preferredMaxDisplayRefreshRate).isEqualTo(0);
+ assertThat(lp.preferredMinDisplayRefreshRate).isEqualTo(0);
+ }
+
+ private void setKeyguardShowing() {
+ mNotificationShadeWindowController.setKeyguardShowing(true);
+ mNotificationShadeWindowController.setKeyguardGoingAway(false);
+ mNotificationShadeWindowController.setKeyguardFadingAway(false);
+ mStateListener.getValue().onStateChanged(StatusBarState.KEYGUARD);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
new file mode 100644
index 000000000000..5fb1e79e93e8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.smartspace
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.smartspace.config.BcSmartspaceConfigProvider
+import com.android.systemui.util.mockito.whenever
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class BcSmartspaceConfigProviderTest : SysuiTestCase() {
+ @Mock private lateinit var featureFlags: FeatureFlags
+
+ private lateinit var configProvider: BcSmartspaceConfigProvider
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ configProvider = BcSmartspaceConfigProvider(featureFlags)
+ }
+
+ @Test
+ fun isDefaultDateWeatherDisabled_flagIsTrue_returnsTrue() {
+ whenever(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+
+ assertTrue(configProvider.isDefaultDateWeatherDisabled)
+ }
+
+ @Test
+ fun isDefaultDateWeatherDisabled_flagIsFalse_returnsFalse() {
+ whenever(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
+
+ assertFalse(configProvider.isDefaultDateWeatherDisabled)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
index 001e1f4d8086..c5432c5fe330 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
@@ -27,6 +27,7 @@ import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dreams.smartspace.DreamSmartspaceController
+import com.android.systemui.plugins.BcSmartspaceConfigPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
import com.android.systemui.plugins.FalsingManager
@@ -94,6 +95,8 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
private class TestView(context: Context?) : View(context), SmartspaceView {
override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {}
+ override fun registerConfigProvider(plugin: BcSmartspaceConfigPlugin?) {}
+
override fun setPrimaryTextColor(color: Int) {}
override fun setIsDreaming(isDreaming: Boolean) {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index ddcf59ec001f..4bcb54ddbbc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -36,6 +36,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.BcSmartspaceConfigPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
@@ -115,6 +116,9 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
private lateinit var plugin: BcSmartspaceDataPlugin
@Mock
+ private lateinit var configPlugin: BcSmartspaceConfigPlugin
+
+ @Mock
private lateinit var controllerListener: SmartspaceTargetListener
@Captor
@@ -209,7 +213,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
executor,
bgExecutor,
handler,
- Optional.of(plugin)
+ Optional.of(plugin),
+ Optional.of(configPlugin),
)
verify(deviceProvisionedController).addCallback(capture(deviceProvisionedCaptor))
@@ -520,6 +525,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
verify(smartspaceManager, never()).createSmartspaceSession(any())
verify(smartspaceView2).setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
verify(smartspaceView2).registerDataProvider(plugin)
+ verify(smartspaceView2).registerConfigProvider(configPlugin)
}
@Test
@@ -557,6 +563,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
verify(smartspaceView).setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
verify(smartspaceView).registerDataProvider(plugin)
+ verify(smartspaceView).registerConfigProvider(configPlugin)
verify(smartspaceSession)
.addOnTargetsAvailableListener(any(), capture(sessionListenerCaptor))
sessionListener = sessionListenerCaptor.value
@@ -638,6 +645,9 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
}
+ override fun registerConfigProvider(plugin: BcSmartspaceConfigPlugin?) {
+ }
+
override fun setPrimaryTextColor(color: Int) {
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 09f8a10f88c7..a8690388c3e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -39,7 +39,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
@@ -137,7 +136,6 @@ public class ShadeListBuilderTest extends SysuiTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
allowTestableLooperAsMainThread();
- when(mNotifPipelineFlags.isStabilityIndexFixEnabled()).thenReturn(true);
mListBuilder = new ShadeListBuilder(
mDumpManager,
@@ -1998,29 +1996,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
@Test
- public void testActiveOrdering_withLegacyStability() {
- when(mNotifPipelineFlags.isSemiStableSortEnabled()).thenReturn(false);
- assertOrder("ABCDEFG", "ABCDEFG", "ABCDEFG", true); // no change
- assertOrder("ABCDEFG", "ACDEFXBG", "ACDEFXBG", true); // X
- assertOrder("ABCDEFG", "ACDEFBG", "ACDEFBG", true); // no change
- assertOrder("ABCDEFG", "ACDEFBXZG", "ACDEFBXZG", true); // Z and X
- assertOrder("ABCDEFG", "AXCDEZFBG", "AXCDEZFBG", true); // Z and X + gap
- }
-
- @Test
- public void testStableOrdering_withLegacyStability() {
- when(mNotifPipelineFlags.isSemiStableSortEnabled()).thenReturn(false);
- mStabilityManager.setAllowEntryReordering(false);
- assertOrder("ABCDEFG", "ABCDEFG", "ABCDEFG", true); // no change
- assertOrder("ABCDEFG", "ACDEFXBG", "XABCDEFG", false); // X
- assertOrder("ABCDEFG", "ACDEFBG", "ABCDEFG", false); // no change
- assertOrder("ABCDEFG", "ACDEFBXZG", "XZABCDEFG", false); // Z and X
- assertOrder("ABCDEFG", "AXCDEZFBG", "XZABCDEFG", false); // Z and X + gap
- }
-
- @Test
public void testStableOrdering() {
- when(mNotifPipelineFlags.isSemiStableSortEnabled()).thenReturn(true);
mStabilityManager.setAllowEntryReordering(false);
// No input or output
assertOrder("", "", "", true);
@@ -2076,7 +2052,6 @@ public class ShadeListBuilderTest extends SysuiTestCase {
@Test
public void testActiveOrdering() {
- when(mNotifPipelineFlags.isSemiStableSortEnabled()).thenReturn(true);
assertOrder("ABCDEFG", "ACDEFXBG", "ACDEFXBG", true); // X
assertOrder("ABCDEFG", "ACDEFBG", "ACDEFBG", true); // no change
assertOrder("ABCDEFG", "ACDEFBXZG", "ACDEFBXZG", true); // Z and X
@@ -2133,7 +2108,6 @@ public class ShadeListBuilderTest extends SysuiTestCase {
@Test
public void stableOrderingDisregardedWithSectionChange() {
- when(mNotifPipelineFlags.isSemiStableSortEnabled()).thenReturn(true);
// GIVEN the first sectioner's packages can be changed from run-to-run
List<String> mutableSectionerPackages = new ArrayList<>();
mutableSectionerPackages.add(PACKAGE_1);
@@ -2229,49 +2203,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
@Test
- public void groupRevertingToSummaryDoesNotRetainStablePositionWithLegacyIndexLogic() {
- when(mNotifPipelineFlags.isStabilityIndexFixEnabled()).thenReturn(false);
-
- // GIVEN a notification group is on screen
- mStabilityManager.setAllowEntryReordering(false);
-
- // WHEN the list is originally built with reordering disabled (and section changes allowed)
- addNotif(0, PACKAGE_1).setRank(2);
- addNotif(1, PACKAGE_1).setRank(3);
- addGroupSummary(2, PACKAGE_1, "group").setRank(4);
- addGroupChild(3, PACKAGE_1, "group").setRank(5);
- addGroupChild(4, PACKAGE_1, "group").setRank(6);
- dispatchBuild();
-
- verifyBuiltList(
- notif(0),
- notif(1),
- group(
- summary(2),
- child(3),
- child(4)
- )
- );
-
- // WHEN the notification summary rank increases and children removed
- setNewRank(notif(2).entry, 1);
- mEntrySet.remove(4);
- mEntrySet.remove(3);
- dispatchBuild();
-
- // VERIFY the summary (incorrectly) moves to the top of the section where it is ranked,
- // despite visual stability being active
- verifyBuiltList(
- notif(2),
- notif(0),
- notif(1)
- );
- }
-
- @Test
public void groupRevertingToSummaryRetainsStablePosition() {
- when(mNotifPipelineFlags.isStabilityIndexFixEnabled()).thenReturn(true);
-
// GIVEN a notification group is on screen
mStabilityManager.setAllowEntryReordering(false);
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 9d531a165f1f..4559a23a4f5c 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
@@ -44,16 +44,23 @@ import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.NotificationChannel;
import android.graphics.Color;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.Drawable;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.DisplayMetrics;
import android.view.View;
+import android.widget.ImageView;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.internal.widget.CachingIconView;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
@@ -61,6 +68,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.row.ExpandableView.OnHeightChangedListener;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import org.junit.Assert;
@@ -72,6 +80,7 @@ import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Arrays;
import java.util.List;
@SmallTest
@@ -96,6 +105,9 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
mDependency,
TestableLooper.get(this));
mNotificationTestHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL);
+ FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
+ fakeFeatureFlags.set(Flags.NOTIFICATION_ANIMATE_BIG_PICTURE, true);
+ mNotificationTestHelper.setFeatureFlags(fakeFeatureFlags);
// create a standard private notification row
Notification normalNotif = mNotificationTestHelper.createNotification();
normalNotif.publicVersion = null;
@@ -559,4 +571,123 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
Assert.assertEquals(1f, mGroupRow.getBottomRoundness(), 0.001f);
Assert.assertEquals(1f, mGroupRow.getChildrenContainer().getBottomRoundness(), 0.001f);
}
+
+ @Test
+ public void testSetContentAnimationRunning_Run() throws Exception {
+ // Create views for the notification row.
+ NotificationContentView publicLayout = mock(NotificationContentView.class);
+ mNotifRow.setPublicLayout(publicLayout);
+ NotificationContentView privateLayout = mock(NotificationContentView.class);
+ mNotifRow.setPrivateLayout(privateLayout);
+
+ mNotifRow.setAnimationRunning(true);
+ verify(publicLayout, times(1)).setContentAnimationRunning(true);
+ verify(privateLayout, times(1)).setContentAnimationRunning(true);
+ }
+
+ @Test
+ public void testSetContentAnimationRunning_Stop() {
+ // Create views for the notification row.
+ NotificationContentView publicLayout = mock(NotificationContentView.class);
+ mNotifRow.setPublicLayout(publicLayout);
+ NotificationContentView privateLayout = mock(NotificationContentView.class);
+ mNotifRow.setPrivateLayout(privateLayout);
+
+ mNotifRow.setAnimationRunning(false);
+ verify(publicLayout, times(1)).setContentAnimationRunning(false);
+ verify(privateLayout, times(1)).setContentAnimationRunning(false);
+ }
+
+ @Test
+ public void testSetContentAnimationRunningInGroupChild_Run() {
+ // Creates parent views on mGroupRow.
+ NotificationContentView publicParentLayout = mock(NotificationContentView.class);
+ mGroupRow.setPublicLayout(publicParentLayout);
+ NotificationContentView privateParentLayout = mock(NotificationContentView.class);
+ mGroupRow.setPrivateLayout(privateParentLayout);
+
+ // Create child views on mNotifRow.
+ NotificationContentView publicChildLayout = mock(NotificationContentView.class);
+ mNotifRow.setPublicLayout(publicChildLayout);
+ NotificationContentView privateChildLayout = mock(NotificationContentView.class);
+ mNotifRow.setPrivateLayout(privateChildLayout);
+ when(mNotifRow.isGroupExpanded()).thenReturn(true);
+ setMockChildrenContainer(mGroupRow, mNotifRow);
+
+ mGroupRow.setAnimationRunning(true);
+ verify(publicParentLayout, times(1)).setContentAnimationRunning(true);
+ verify(privateParentLayout, times(1)).setContentAnimationRunning(true);
+ // The child layouts should be started too.
+ verify(publicChildLayout, times(1)).setContentAnimationRunning(true);
+ verify(privateChildLayout, times(1)).setContentAnimationRunning(true);
+ }
+
+
+ @Test
+ public void testSetIconAnimationRunningGroup_Run() {
+ // Create views for a group row.
+ NotificationContentView publicParentLayout = mock(NotificationContentView.class);
+ mGroupRow.setPublicLayout(publicParentLayout);
+ NotificationContentView privateParentLayout = mock(NotificationContentView.class);
+ mGroupRow.setPrivateLayout(privateParentLayout);
+ when(mGroupRow.isGroupExpanded()).thenReturn(true);
+
+ // Sets up mNotifRow as a child ExpandableNotificationRow.
+ NotificationContentView publicChildLayout = mock(NotificationContentView.class);
+ mNotifRow.setPublicLayout(publicChildLayout);
+ NotificationContentView privateChildLayout = mock(NotificationContentView.class);
+ mNotifRow.setPrivateLayout(privateChildLayout);
+ when(mNotifRow.isGroupExpanded()).thenReturn(true);
+
+ NotificationChildrenContainer mockContainer =
+ setMockChildrenContainer(mGroupRow, mNotifRow);
+
+ // Mock the children view wrappers, and give them each an icon.
+ NotificationViewWrapper mockViewWrapper = mock(NotificationViewWrapper.class);
+ when(mockContainer.getNotificationViewWrapper()).thenReturn(mockViewWrapper);
+ CachingIconView mockIcon = mock(CachingIconView.class);
+ when(mockViewWrapper.getIcon()).thenReturn(mockIcon);
+
+ NotificationViewWrapper mockLowPriorityViewWrapper = mock(NotificationViewWrapper.class);
+ when(mockContainer.getLowPriorityViewWrapper()).thenReturn(mockLowPriorityViewWrapper);
+ CachingIconView mockLowPriorityIcon = mock(CachingIconView.class);
+ when(mockLowPriorityViewWrapper.getIcon()).thenReturn(mockLowPriorityIcon);
+
+ // Give the icon image views drawables, so we can make sure they animate.
+ // We use both AnimationDrawables and AnimatedVectorDrawables to ensure both work.
+ AnimationDrawable drawable = mock(AnimationDrawable.class);
+ AnimatedVectorDrawable vectorDrawable = mock(AnimatedVectorDrawable.class);
+ setDrawableIconsInImageView(mockIcon, drawable, vectorDrawable);
+
+ AnimationDrawable lowPriDrawable = mock(AnimationDrawable.class);
+ AnimatedVectorDrawable lowPriVectorDrawable = mock(AnimatedVectorDrawable.class);
+ setDrawableIconsInImageView(mockLowPriorityIcon, lowPriDrawable, lowPriVectorDrawable);
+
+ mGroupRow.setAnimationRunning(true);
+ verify(drawable, times(1)).start();
+ verify(vectorDrawable, times(1)).start();
+ verify(lowPriDrawable, times(1)).start();
+ verify(lowPriVectorDrawable, times(1)).start();
+ }
+
+ private void setDrawableIconsInImageView(CachingIconView icon, Drawable iconDrawable,
+ Drawable rightIconDrawable) {
+ ImageView iconView = mock(ImageView.class);
+ when(icon.findViewById(com.android.internal.R.id.icon)).thenReturn(iconView);
+ when(iconView.getDrawable()).thenReturn(iconDrawable);
+
+ ImageView rightIconView = mock(ImageView.class);
+ when(icon.findViewById(com.android.internal.R.id.right_icon)).thenReturn(rightIconView);
+ when(rightIconView.getDrawable()).thenReturn(rightIconDrawable);
+ }
+
+ private NotificationChildrenContainer setMockChildrenContainer(
+ ExpandableNotificationRow parentRow, ExpandableNotificationRow childRow) {
+ List<ExpandableNotificationRow> rowList = Arrays.asList(childRow);
+ NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
+ when(mockContainer.getNotificationChildCount()).thenReturn(1);
+ when(mockContainer.getAttachedChildren()).thenReturn(rowList);
+ parentRow.setChildrenContainer(mockContainer);
+ return mockContainer;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
index 562b4dfb35ef..7b2051da4d15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
@@ -34,9 +34,12 @@ import com.android.systemui.media.dialog.MediaOutputDialogFactory
import com.android.systemui.statusbar.notification.FeedbackIcon
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -44,6 +47,7 @@ import org.mockito.Mock
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations.initMocks
@@ -305,6 +309,86 @@ class NotificationContentViewTest : SysuiTestCase() {
assertEquals(0, getMarginBottom(actionListMarginTarget))
}
+ @Test
+ fun onSetAnimationRunning() {
+ // Given: contractedWrapper, enpandedWrapper, and headsUpWrapper being set
+ val mockContracted = mock<NotificationViewWrapper>()
+ val mockExpanded = mock<NotificationViewWrapper>()
+ val mockHeadsUp = mock<NotificationViewWrapper>()
+
+ view.setContractedWrapper(mockContracted)
+ view.setExpandedWrapper(mockExpanded)
+ view.setHeadsUpWrapper(mockHeadsUp)
+
+ // When: we set content animation running.
+ assertTrue(view.setContentAnimationRunning(true))
+
+ // Then: contractedChild, expandedChild, and headsUpChild should have setAnimationsRunning
+ // called on them.
+ verify(mockContracted, times(1)).setAnimationsRunning(true)
+ verify(mockExpanded, times(1)).setAnimationsRunning(true)
+ verify(mockHeadsUp, times(1)).setAnimationsRunning(true)
+
+ // When: we set content animation running true _again_.
+ assertFalse(view.setContentAnimationRunning(true))
+
+ // Then: the children should not have setAnimationRunning called on them again.
+ // Verify counts number of calls so far on the object, so these still register as 1.
+ verify(mockContracted, times(1)).setAnimationsRunning(true)
+ verify(mockExpanded, times(1)).setAnimationsRunning(true)
+ verify(mockHeadsUp, times(1)).setAnimationsRunning(true)
+ }
+
+ @Test
+ fun onSetAnimationStopped() {
+ // Given: contractedWrapper, expandedWrapper, and headsUpWrapper being set
+ val mockContracted = mock<NotificationViewWrapper>()
+ val mockExpanded = mock<NotificationViewWrapper>()
+ val mockHeadsUp = mock<NotificationViewWrapper>()
+
+ view.setContractedWrapper(mockContracted)
+ view.setExpandedWrapper(mockExpanded)
+ view.setHeadsUpWrapper(mockHeadsUp)
+
+ // When: we set content animation running.
+ assertTrue(view.setContentAnimationRunning(true))
+
+ // Then: contractedChild, expandedChild, and headsUpChild should have setAnimationsRunning
+ // called on them.
+ verify(mockContracted).setAnimationsRunning(true)
+ verify(mockExpanded).setAnimationsRunning(true)
+ verify(mockHeadsUp).setAnimationsRunning(true)
+
+ // When: we set content animation running false, the state changes, so the function
+ // returns true.
+ assertTrue(view.setContentAnimationRunning(false))
+
+ // Then: the children have their animations stopped.
+ verify(mockContracted).setAnimationsRunning(false)
+ verify(mockExpanded).setAnimationsRunning(false)
+ verify(mockHeadsUp).setAnimationsRunning(false)
+ }
+
+ @Test
+ fun onSetAnimationInitStopped() {
+ // Given: contractedWrapper, expandedWrapper, and headsUpWrapper being set
+ val mockContracted = mock<NotificationViewWrapper>()
+ val mockExpanded = mock<NotificationViewWrapper>()
+ val mockHeadsUp = mock<NotificationViewWrapper>()
+
+ view.setContractedWrapper(mockContracted)
+ view.setExpandedWrapper(mockExpanded)
+ view.setHeadsUpWrapper(mockHeadsUp)
+
+ // When: we try to stop the animations before they've been started.
+ assertFalse(view.setContentAnimationRunning(false))
+
+ // Then: the children should not have setAnimationRunning called on them again.
+ verify(mockContracted, never()).setAnimationsRunning(false)
+ verify(mockExpanded, never()).setAnimationsRunning(false)
+ verify(mockHeadsUp, never()).setAnimationsRunning(false)
+ }
+
private fun createMockContainingNotification(notificationEntry: NotificationEntry) =
mock<ExpandableNotificationRow>().apply {
whenever(this.entry).thenReturn(notificationEntry)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 728e0265c729..e4fc4d5d54ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -53,6 +53,7 @@ import com.android.internal.logging.UiEventLogger;
import com.android.systemui.TestableDependency;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -130,6 +131,7 @@ public class NotificationTestHelper {
public final OnUserInteractionCallback mOnUserInteractionCallback;
public final Runnable mFutureDismissalRunnable;
private @InflationFlag int mDefaultInflationFlags;
+ private FeatureFlags mFeatureFlags;
public NotificationTestHelper(
Context context,
@@ -193,12 +195,17 @@ public class NotificationTestHelper {
mFutureDismissalRunnable = mock(Runnable.class);
when(mOnUserInteractionCallback.registerFutureDismissal(any(), anyInt()))
.thenReturn(mFutureDismissalRunnable);
+ mFeatureFlags = mock(FeatureFlags.class);
}
public void setDefaultInflationFlags(@InflationFlag int defaultInflationFlags) {
mDefaultInflationFlags = defaultInflationFlags;
}
+ public void setFeatureFlags(FeatureFlags featureFlags) {
+ mFeatureFlags = featureFlags;
+ }
+
public ExpandableNotificationRowLogger getMockLogger() {
return mMockLogger;
}
@@ -561,7 +568,8 @@ public class NotificationTestHelper {
mock(NotificationGutsManager.class),
mock(MetricsLogger.class),
mock(SmartReplyConstants.class),
- mock(SmartReplyController.class));
+ mock(SmartReplyController.class),
+ mFeatureFlags);
row.setAboveShelfChangedListener(aboveShelf -> { });
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java
index 509ba4194e5e..8f88501a38f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java
@@ -16,11 +16,12 @@
package com.android.systemui.statusbar.notification.row.wrapper;
+import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
import android.app.Notification;
-import android.content.Context;
+import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.testing.AndroidTestingRunner;
@@ -28,11 +29,11 @@ import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.TextView;
import androidx.test.filters.SmallTest;
+import com.android.internal.R;
+import com.android.internal.widget.BigPictureNotificationImageView;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -73,4 +74,38 @@ public class NotificationBigPictureTemplateViewWrapperTest extends SysuiTestCase
Notification.EXTRA_LARGE_ICON_BIG, new Bundle());
wrapper.onContentUpdated(mRow);
}
+
+ @Test
+ public void setAnimationsRunning_Run() {
+ BigPictureNotificationImageView imageView = mView.findViewById(R.id.big_picture);
+ AnimatedImageDrawable mockDrawable = mock(AnimatedImageDrawable.class);
+
+ assertNotNull(imageView);
+ imageView.setImageDrawable(mockDrawable);
+
+ NotificationViewWrapper wrapper = new NotificationBigPictureTemplateViewWrapper(mContext,
+ mView, mRow);
+ // Required to re-initialize the imageView to the imageView created above.
+ wrapper.onContentUpdated(mRow);
+
+ wrapper.setAnimationsRunning(true);
+ verify(mockDrawable).start();
+ }
+
+ @Test
+ public void setAnimationsRunning_Stop() {
+ BigPictureNotificationImageView imageView = mView.findViewById(R.id.big_picture);
+ AnimatedImageDrawable mockDrawable = mock(AnimatedImageDrawable.class);
+
+ assertNotNull(imageView);
+ imageView.setImageDrawable(mockDrawable);
+
+ NotificationViewWrapper wrapper = new NotificationBigPictureTemplateViewWrapper(mContext,
+ mView, mRow);
+ // Required to re-initialize the imageView to the imageView created above.
+ wrapper.onContentUpdated(mRow);
+
+ wrapper.setAnimationsRunning(false);
+ verify(mockDrawable).stop();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
new file mode 100644
index 000000000000..3fa68bb69da2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper
+
+import android.graphics.drawable.AnimatedImageDrawable
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.internal.widget.CachingIconView
+import com.android.internal.widget.ConversationLayout
+import com.android.internal.widget.MessagingGroup
+import com.android.internal.widget.MessagingImageMessage
+import com.android.internal.widget.MessagingLinearLayout
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationConversationTemplateViewWrapperTest : SysuiTestCase() {
+
+ private lateinit var mRow: ExpandableNotificationRow
+ private lateinit var helper: NotificationTestHelper
+
+ @Before
+ fun setUp() {
+ allowTestableLooperAsMainThread()
+ helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+ mRow = helper.createRow()
+ }
+
+ @Test
+ fun setAnimationsRunning_Run() {
+ // Creates a mocked out NotificationEntry of ConversationLayout type,
+ // with a mock imageMessage.drawable embedded in its MessagingImageMessages
+ // (both top level, and in a group).
+ val mockDrawable = mock<AnimatedImageDrawable>()
+ val mockDrawable2 = mock<AnimatedImageDrawable>()
+ val mockLayoutView: View = fakeConversationLayout(mockDrawable, mockDrawable2)
+
+ val wrapper: NotificationViewWrapper =
+ NotificationConversationTemplateViewWrapper(mContext, mockLayoutView, mRow)
+ wrapper.onContentUpdated(mRow)
+ wrapper.setAnimationsRunning(true)
+
+ // Verifies that each AnimatedImageDrawable is started animating.
+ verify(mockDrawable).start()
+ verify(mockDrawable2).start()
+ }
+
+ @Test
+ fun setAnimationsRunning_Stop() {
+ // Creates a mocked out NotificationEntry of ConversationLayout type,
+ // with a mock imageMessage.drawable embedded in its MessagingImageMessages
+ // (both top level, and in a group).
+ val mockDrawable = mock<AnimatedImageDrawable>()
+ val mockDrawable2 = mock<AnimatedImageDrawable>()
+ val mockLayoutView: View = fakeConversationLayout(mockDrawable, mockDrawable2)
+
+ val wrapper: NotificationViewWrapper =
+ NotificationConversationTemplateViewWrapper(mContext, mockLayoutView, mRow)
+ wrapper.onContentUpdated(mRow)
+ wrapper.setAnimationsRunning(false)
+
+ // Verifies that each AnimatedImageDrawable is started animating.
+ verify(mockDrawable).stop()
+ verify(mockDrawable2).stop()
+ }
+
+ private fun fakeConversationLayout(
+ mockDrawableGroupMessage: AnimatedImageDrawable,
+ mockDrawableImageMessage: AnimatedImageDrawable
+ ): View {
+ val mockMessagingImageMessage: MessagingImageMessage =
+ mock<MessagingImageMessage>().apply {
+ whenever(drawable).thenReturn(mockDrawableImageMessage)
+ }
+ val mockImageMessageContainer: MessagingLinearLayout =
+ mock<MessagingLinearLayout>().apply {
+ whenever(childCount).thenReturn(1)
+ whenever(getChildAt(any())).thenReturn(mockMessagingImageMessage)
+ }
+
+ val mockMessagingImageMessageForGroup: MessagingImageMessage =
+ mock<MessagingImageMessage>().apply {
+ whenever(drawable).thenReturn(mockDrawableGroupMessage)
+ }
+ val mockMessageContainer: MessagingLinearLayout =
+ mock<MessagingLinearLayout>().apply {
+ whenever(childCount).thenReturn(1)
+ whenever(getChildAt(any())).thenReturn(mockMessagingImageMessageForGroup)
+ }
+ val mockGroup: MessagingGroup =
+ mock<MessagingGroup>().apply {
+ whenever(messageContainer).thenReturn(mockMessageContainer)
+ }
+ val mockView: View =
+ mock<ConversationLayout>().apply {
+ whenever(messagingGroups).thenReturn(ArrayList<MessagingGroup>(listOf(mockGroup)))
+ whenever(imageMessageContainer).thenReturn(mockImageMessageContainer)
+ whenever(messagingLinearLayout).thenReturn(mockMessageContainer)
+
+ // These must be mocked as they're required to be nonnull.
+ whenever(requireViewById<View>(R.id.conversation_icon_container)).thenReturn(mock())
+ whenever(requireViewById<CachingIconView>(R.id.conversation_icon))
+ .thenReturn(mock())
+ whenever(findViewById<CachingIconView>(R.id.icon)).thenReturn(mock())
+ whenever(requireViewById<View>(R.id.conversation_icon_badge_bg)).thenReturn(mock())
+ whenever(requireViewById<View>(R.id.expand_button)).thenReturn(mock())
+ whenever(requireViewById<View>(R.id.expand_button_container)).thenReturn(mock())
+ whenever(requireViewById<View>(R.id.conversation_icon_badge_ring))
+ .thenReturn(mock())
+ whenever(requireViewById<View>(R.id.app_name_text)).thenReturn(mock())
+ whenever(requireViewById<View>(R.id.conversation_text)).thenReturn(mock())
+ }
+ return mockView
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt
new file mode 100644
index 000000000000..c0444b563a2c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper
+
+import android.graphics.drawable.AnimatedImageDrawable
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.internal.widget.MessagingGroup
+import com.android.internal.widget.MessagingImageMessage
+import com.android.internal.widget.MessagingLayout
+import com.android.internal.widget.MessagingLinearLayout
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationMessagingTemplateViewWrapperTest : SysuiTestCase() {
+
+ private lateinit var mRow: ExpandableNotificationRow
+ private lateinit var helper: NotificationTestHelper
+
+ @Before
+ fun setUp() {
+ allowTestableLooperAsMainThread()
+ helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+ mRow = helper.createRow()
+ }
+
+ @Test
+ fun setAnimationsRunning_Run() {
+ // Creates a mocked out NotificationEntry of MessagingLayout/MessagingStyle type,
+ // with a mock imageMessage.drawable embedded in its MessagingImageMessage.
+ val mockDrawable = mock<AnimatedImageDrawable>()
+ val mockLayoutView: View = fakeMessagingLayout(mockDrawable)
+
+ val wrapper: NotificationViewWrapper =
+ NotificationMessagingTemplateViewWrapper(mContext, mockLayoutView, mRow)
+ wrapper.setAnimationsRunning(true)
+
+ // Verifies that each AnimatedImageDrawable is started animating.
+ verify(mockDrawable).start()
+ }
+
+ @Test
+ fun setAnimationsRunning_Stop() {
+ // Creates a mocked out NotificationEntry of MessagingLayout/MessagingStyle type,
+ // with a mock imageMessage.drawable embedded in its MessagingImageMessage.
+ val mockDrawable = mock<AnimatedImageDrawable>()
+ val mockLayoutView: View = fakeMessagingLayout(mockDrawable)
+
+ val wrapper: NotificationViewWrapper =
+ NotificationMessagingTemplateViewWrapper(mContext, mockLayoutView, mRow)
+ wrapper.setAnimationsRunning(false)
+
+ // Verifies that each AnimatedImageDrawable is started animating.
+ verify(mockDrawable).stop()
+ }
+
+ private fun fakeMessagingLayout(mockDrawable: AnimatedImageDrawable): View {
+ val mockMessagingImageMessage: MessagingImageMessage =
+ mock<MessagingImageMessage>().apply { whenever(drawable).thenReturn(mockDrawable) }
+ val mockMessageContainer: MessagingLinearLayout =
+ mock<MessagingLinearLayout>().apply {
+ whenever(childCount).thenReturn(1)
+ whenever(getChildAt(any())).thenReturn(mockMessagingImageMessage)
+ }
+ val mockGroup: MessagingGroup =
+ mock<MessagingGroup>().apply {
+ whenever(messageContainer).thenReturn(mockMessageContainer)
+ }
+ val mockView: View =
+ mock<MessagingLayout>().apply {
+ whenever(messagingGroups).thenReturn(ArrayList<MessagingGroup>(listOf(mockGroup)))
+ }
+ return mockView
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index c8157ccc8a9a..4c1b219af843 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -544,6 +544,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mCentralSurfaces.startKeyguard();
mInitController.executePostInitTasks();
notificationLogger.setUpWithContainer(mNotificationListContainer);
+ mCentralSurfaces.registerCallbacks();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index df7ee432e79e..2f495355df40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN;
-import static com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE;
+import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
+import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
import static com.google.common.truth.Truth.assertThat;
@@ -58,8 +58,8 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Assert;
@@ -87,7 +87,7 @@ public class KeyguardBouncerTest extends SysuiTestCase {
@Mock
private KeyguardHostViewController mKeyguardHostViewController;
@Mock
- private KeyguardBouncer.PrimaryBouncerExpansionCallback mExpansionCallback;
+ private PrimaryBouncerExpansionCallback mExpansionCallback;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
new file mode 100644
index 000000000000..3e90ed9811d0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.content.pm.PackageManager
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.policy.DevicePostureController
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_FLIPPED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.tuner.TunerService
+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.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class KeyguardBypassControllerTest : SysuiTestCase() {
+
+ private lateinit var keyguardBypassController: KeyguardBypassController
+ private lateinit var postureControllerCallback: DevicePostureController.Callback
+ @Mock private lateinit var tunerService: TunerService
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var lockscreenUserManager: NotificationLockscreenUserManager
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
+ @Mock private lateinit var devicePostureController: DevicePostureController
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var packageManager: PackageManager
+ @Captor
+ private val postureCallbackCaptor =
+ ArgumentCaptor.forClass(DevicePostureController.Callback::class.java)
+ @JvmField @Rule val mockito = MockitoJUnit.rule()
+
+ @Before
+ fun setUp() {
+ context.setMockPackageManager(packageManager)
+ whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true)
+ whenever(keyguardStateController.isFaceAuthEnabled).thenReturn(true)
+ }
+
+ @After
+ fun tearDown() {
+ reset(devicePostureController)
+ reset(keyguardStateController)
+ }
+
+ private fun defaultConfigPostureClosed() {
+ context.orCreateTestableResources.addOverride(
+ R.integer.config_face_auth_supported_posture,
+ DEVICE_POSTURE_CLOSED
+ )
+ initKeyguardBypassController()
+ verify(devicePostureController).addCallback(postureCallbackCaptor.capture())
+ postureControllerCallback = postureCallbackCaptor.value
+ }
+
+ private fun defaultConfigPostureOpened() {
+ context.orCreateTestableResources.addOverride(
+ R.integer.config_face_auth_supported_posture,
+ DEVICE_POSTURE_OPENED
+ )
+ initKeyguardBypassController()
+ verify(devicePostureController).addCallback(postureCallbackCaptor.capture())
+ postureControllerCallback = postureCallbackCaptor.value
+ }
+
+ private fun defaultConfigPostureFlipped() {
+ context.orCreateTestableResources.addOverride(
+ R.integer.config_face_auth_supported_posture,
+ DEVICE_POSTURE_FLIPPED
+ )
+ initKeyguardBypassController()
+ verify(devicePostureController).addCallback(postureCallbackCaptor.capture())
+ postureControllerCallback = postureCallbackCaptor.value
+ }
+
+ private fun defaultConfigPostureUnknown() {
+ context.orCreateTestableResources.addOverride(
+ R.integer.config_face_auth_supported_posture,
+ DEVICE_POSTURE_UNKNOWN
+ )
+ initKeyguardBypassController()
+ verify(devicePostureController, never()).addCallback(postureCallbackCaptor.capture())
+ }
+
+ private fun initKeyguardBypassController() {
+ keyguardBypassController =
+ KeyguardBypassController(
+ context,
+ tunerService,
+ statusBarStateController,
+ lockscreenUserManager,
+ keyguardStateController,
+ shadeExpansionStateManager,
+ devicePostureController,
+ dumpManager
+ )
+ }
+
+ @Test
+ fun configDevicePostureClosed_matchState_isPostureAllowedForFaceAuth_returnTrue() {
+ defaultConfigPostureClosed()
+
+ postureControllerCallback.onPostureChanged(DEVICE_POSTURE_CLOSED)
+
+ assertThat(keyguardBypassController.isPostureAllowedForFaceAuth()).isTrue()
+ }
+
+ @Test
+ fun configDevicePostureOpen_matchState_isPostureAllowedForFaceAuth_returnTrue() {
+ defaultConfigPostureOpened()
+
+ postureControllerCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
+
+ assertThat(keyguardBypassController.isPostureAllowedForFaceAuth()).isTrue()
+ }
+
+ @Test
+ fun configDevicePostureFlipped_matchState_isPostureAllowedForFaceAuth_returnTrue() {
+ defaultConfigPostureFlipped()
+
+ postureControllerCallback.onPostureChanged(DEVICE_POSTURE_FLIPPED)
+
+ assertThat(keyguardBypassController.isPostureAllowedForFaceAuth()).isTrue()
+ }
+
+ @Test
+ fun configDevicePostureClosed_changeOpened_isPostureAllowedForFaceAuth_returnFalse() {
+ defaultConfigPostureClosed()
+
+ postureControllerCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
+
+ assertThat(keyguardBypassController.isPostureAllowedForFaceAuth()).isFalse()
+ }
+
+ @Test
+ fun configDevicePostureClosed_changeFlipped_isPostureAllowedForFaceAuth_returnFalse() {
+ defaultConfigPostureClosed()
+
+ postureControllerCallback.onPostureChanged(DEVICE_POSTURE_FLIPPED)
+
+ assertThat(keyguardBypassController.isPostureAllowedForFaceAuth()).isFalse()
+ }
+
+ @Test
+ fun configDevicePostureOpened_changeClosed_isPostureAllowedForFaceAuth_returnFalse() {
+ defaultConfigPostureOpened()
+
+ postureControllerCallback.onPostureChanged(DEVICE_POSTURE_CLOSED)
+
+ assertThat(keyguardBypassController.isPostureAllowedForFaceAuth()).isFalse()
+ }
+
+ @Test
+ fun configDevicePostureOpened_changeFlipped_isPostureAllowedForFaceAuth_returnFalse() {
+ defaultConfigPostureOpened()
+
+ postureControllerCallback.onPostureChanged(DEVICE_POSTURE_FLIPPED)
+
+ assertThat(keyguardBypassController.isPostureAllowedForFaceAuth()).isFalse()
+ }
+
+ @Test
+ fun configDevicePostureFlipped_changeClosed_isPostureAllowedForFaceAuth_returnFalse() {
+ defaultConfigPostureFlipped()
+
+ postureControllerCallback.onPostureChanged(DEVICE_POSTURE_CLOSED)
+
+ assertThat(keyguardBypassController.isPostureAllowedForFaceAuth()).isFalse()
+ }
+
+ @Test
+ fun configDevicePostureFlipped_changeOpened_isPostureAllowedForFaceAuth_returnFalse() {
+ defaultConfigPostureFlipped()
+
+ postureControllerCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
+
+ assertThat(keyguardBypassController.isPostureAllowedForFaceAuth()).isFalse()
+ }
+
+ @Test
+ fun defaultConfigPostureClosed_canOverrideByPassAlways_shouldReturnFalse() {
+ context.orCreateTestableResources.addOverride(
+ R.integer.config_face_unlock_bypass_override,
+ 1 /* FACE_UNLOCK_BYPASS_ALWAYS */
+ )
+
+ defaultConfigPostureClosed()
+
+ postureControllerCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
+
+ assertThat(keyguardBypassController.bypassEnabled).isFalse()
+ }
+
+ @Test
+ fun defaultConfigPostureUnknown_canNotOverrideByPassAlways_shouldReturnTrue() {
+ context.orCreateTestableResources.addOverride(
+ R.integer.config_face_unlock_bypass_override,
+ 1 /* FACE_UNLOCK_BYPASS_ALWAYS */
+ )
+
+ defaultConfigPostureUnknown()
+
+ assertThat(keyguardBypassController.bypassEnabled).isTrue()
+ }
+
+ @Test
+ fun defaultConfigPostureUnknown_canNotOverrideByPassNever_shouldReturnFalse() {
+ context.orCreateTestableResources.addOverride(
+ R.integer.config_face_unlock_bypass_override,
+ 2 /* FACE_UNLOCK_BYPASS_NEVER */
+ )
+
+ defaultConfigPostureUnknown()
+
+ assertThat(keyguardBypassController.bypassEnabled).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index e4759057a59c..c7a0582f0007 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -58,6 +58,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.statusbar.policy.FakeConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -1562,7 +1563,7 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void transitionToDreaming() {
mScrimController.setRawPanelExpansionFraction(0f);
- mScrimController.setBouncerHiddenFraction(KeyguardBouncer.EXPANSION_HIDDEN);
+ mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN);
mScrimController.transitionTo(ScrimState.DREAMING);
finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt
new file mode 100644
index 000000000000..3bc288a2f823
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.os.UserHandle
+import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.StatusBarIcon
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY
+import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl.EXTERNAL_SLOT_SUFFIX
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.verify
+
+@SmallTest
+class StatusBarIconControllerImplTest : SysuiTestCase() {
+
+ private lateinit var underTest: StatusBarIconControllerImpl
+
+ private lateinit var iconList: StatusBarIconList
+ private val iconGroup: StatusBarIconController.IconManager = mock()
+
+ @Before
+ fun setUp() {
+ iconList = StatusBarIconList(arrayOf())
+ underTest =
+ StatusBarIconControllerImpl(
+ context,
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ iconList,
+ mock(),
+ )
+ underTest.addIconGroup(iconGroup)
+ }
+
+ /** Regression test for b/255428281. */
+ @Test
+ fun internalAndExternalIconWithSameName_bothDisplayed() {
+ val slotName = "mute"
+
+ // Internal
+ underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
+
+ // External
+ val externalIcon =
+ StatusBarIcon(
+ "external.package",
+ UserHandle.ALL,
+ /* iconId= */ 2,
+ /* iconLevel= */ 0,
+ /* number= */ 0,
+ "contentDescription",
+ )
+ underTest.setIcon(slotName, externalIcon)
+
+ assertThat(iconList.slots).hasSize(2)
+ // Whichever was added last comes first
+ assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
+ assertThat(iconList.slots[1].name).isEqualTo(slotName)
+ assertThat(iconList.slots[0].hasIconsInSlot()).isTrue()
+ assertThat(iconList.slots[1].hasIconsInSlot()).isTrue()
+ }
+
+ /** Regression test for b/255428281. */
+ @Test
+ fun internalAndExternalIconWithSameName_externalRemoved_viaRemoveIcon_internalStays() {
+ val slotName = "mute"
+
+ // Internal
+ underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
+
+ // External
+ underTest.setIcon(slotName, createExternalIcon())
+
+ // WHEN the external icon is removed via #removeIcon
+ underTest.removeIcon(slotName)
+
+ // THEN the external icon is removed but the internal icon remains
+ // Note: [StatusBarIconList] never removes slots from its list, it just sets the holder for
+ // the slot to null when an icon is removed.
+ assertThat(iconList.slots).hasSize(2)
+ assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
+ assertThat(iconList.slots[1].name).isEqualTo(slotName)
+ assertThat(iconList.slots[0].hasIconsInSlot()).isFalse() // Indicates removal
+ assertThat(iconList.slots[1].hasIconsInSlot()).isTrue()
+
+ verify(iconGroup).onRemoveIcon(0)
+ }
+
+ /** Regression test for b/255428281. */
+ @Test
+ fun internalAndExternalIconWithSameName_externalRemoved_viaRemoveAll_internalStays() {
+ val slotName = "mute"
+
+ // Internal
+ underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
+
+ // External
+ underTest.setIcon(slotName, createExternalIcon())
+
+ // WHEN the external icon is removed via #removeAllIconsForExternalSlot
+ underTest.removeAllIconsForExternalSlot(slotName)
+
+ // THEN the external icon is removed but the internal icon remains
+ assertThat(iconList.slots).hasSize(2)
+ assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
+ assertThat(iconList.slots[1].name).isEqualTo(slotName)
+ assertThat(iconList.slots[0].hasIconsInSlot()).isFalse() // Indicates removal
+ assertThat(iconList.slots[1].hasIconsInSlot()).isTrue()
+
+ verify(iconGroup).onRemoveIcon(0)
+ }
+
+ /** Regression test for b/255428281. */
+ @Test
+ fun internalAndExternalIconWithSameName_externalRemoved_viaSetNull_internalStays() {
+ val slotName = "mute"
+
+ // Internal
+ underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
+
+ // External
+ underTest.setIcon(slotName, createExternalIcon())
+
+ // WHEN the external icon is removed via a #setIcon(null)
+ underTest.setIcon(slotName, /* icon= */ null)
+
+ // THEN the external icon is removed but the internal icon remains
+ assertThat(iconList.slots).hasSize(2)
+ assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
+ assertThat(iconList.slots[1].name).isEqualTo(slotName)
+ assertThat(iconList.slots[0].hasIconsInSlot()).isFalse() // Indicates removal
+ assertThat(iconList.slots[1].hasIconsInSlot()).isTrue()
+
+ verify(iconGroup).onRemoveIcon(0)
+ }
+
+ /** Regression test for b/255428281. */
+ @Test
+ fun internalAndExternalIconWithSameName_internalRemoved_viaRemove_externalStays() {
+ val slotName = "mute"
+
+ // Internal
+ underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
+
+ // External
+ underTest.setIcon(slotName, createExternalIcon())
+
+ // WHEN the internal icon is removed via #removeIcon
+ underTest.removeIcon(slotName, /* tag= */ 0)
+
+ // THEN the external icon is removed but the internal icon remains
+ assertThat(iconList.slots).hasSize(2)
+ assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
+ assertThat(iconList.slots[1].name).isEqualTo(slotName)
+ assertThat(iconList.slots[0].hasIconsInSlot()).isTrue()
+ assertThat(iconList.slots[1].hasIconsInSlot()).isFalse() // Indicates removal
+
+ verify(iconGroup).onRemoveIcon(1)
+ }
+
+ /** Regression test for b/255428281. */
+ @Test
+ fun internalAndExternalIconWithSameName_internalRemoved_viaRemoveAll_externalStays() {
+ val slotName = "mute"
+
+ // Internal
+ underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
+
+ // External
+ underTest.setIcon(slotName, createExternalIcon())
+
+ // WHEN the internal icon is removed via #removeAllIconsForSlot
+ underTest.removeAllIconsForSlot(slotName)
+
+ // THEN the external icon is removed but the internal icon remains
+ assertThat(iconList.slots).hasSize(2)
+ assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
+ assertThat(iconList.slots[1].name).isEqualTo(slotName)
+ assertThat(iconList.slots[0].hasIconsInSlot()).isTrue()
+ assertThat(iconList.slots[1].hasIconsInSlot()).isFalse() // Indicates removal
+
+ verify(iconGroup).onRemoveIcon(1)
+ }
+
+ /** Regression test for b/255428281. */
+ @Test
+ fun internalAndExternalIconWithSameName_internalUpdatedIndependently() {
+ val slotName = "mute"
+
+ // Internal
+ underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
+
+ // External
+ val startingExternalIcon =
+ StatusBarIcon(
+ "external.package",
+ UserHandle.ALL,
+ /* iconId= */ 20,
+ /* iconLevel= */ 0,
+ /* number= */ 0,
+ "externalDescription",
+ )
+ underTest.setIcon(slotName, startingExternalIcon)
+
+ // WHEN the internal icon is updated
+ underTest.setIcon(slotName, /* resourceId= */ 11, "newContentDescription")
+
+ // THEN only the internal slot gets the updates
+ val internalSlot = iconList.slots[1]
+ val internalHolder = internalSlot.getHolderForTag(TAG_PRIMARY)!!
+ assertThat(internalSlot.name).isEqualTo(slotName)
+ assertThat(internalHolder.icon!!.contentDescription).isEqualTo("newContentDescription")
+ assertThat(internalHolder.icon!!.icon.resId).isEqualTo(11)
+
+ // And the external slot has its own values
+ val externalSlot = iconList.slots[0]
+ val externalHolder = externalSlot.getHolderForTag(TAG_PRIMARY)!!
+ assertThat(externalSlot.name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
+ assertThat(externalHolder.icon!!.contentDescription).isEqualTo("externalDescription")
+ assertThat(externalHolder.icon!!.icon.resId).isEqualTo(20)
+ }
+
+ /** Regression test for b/255428281. */
+ @Test
+ fun internalAndExternalIconWithSameName_externalUpdatedIndependently() {
+ val slotName = "mute"
+
+ // Internal
+ underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
+
+ // External
+ val startingExternalIcon =
+ StatusBarIcon(
+ "external.package",
+ UserHandle.ALL,
+ /* iconId= */ 20,
+ /* iconLevel= */ 0,
+ /* number= */ 0,
+ "externalDescription",
+ )
+ underTest.setIcon(slotName, startingExternalIcon)
+
+ // WHEN the external icon is updated
+ val newExternalIcon =
+ StatusBarIcon(
+ "external.package",
+ UserHandle.ALL,
+ /* iconId= */ 21,
+ /* iconLevel= */ 0,
+ /* number= */ 0,
+ "newExternalDescription",
+ )
+ underTest.setIcon(slotName, newExternalIcon)
+
+ // THEN only the external slot gets the updates
+ val externalSlot = iconList.slots[0]
+ val externalHolder = externalSlot.getHolderForTag(TAG_PRIMARY)!!
+ assertThat(externalSlot.name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
+ assertThat(externalHolder.icon!!.contentDescription).isEqualTo("newExternalDescription")
+ assertThat(externalHolder.icon!!.icon.resId).isEqualTo(21)
+
+ // And the internal slot has its own values
+ val internalSlot = iconList.slots[1]
+ val internalHolder = internalSlot.getHolderForTag(TAG_PRIMARY)!!
+ assertThat(internalSlot.name).isEqualTo(slotName)
+ assertThat(internalHolder.icon!!.contentDescription).isEqualTo("contentDescription")
+ assertThat(internalHolder.icon!!.icon.resId).isEqualTo(10)
+ }
+
+ @Test
+ fun externalSlot_alreadyEndsWithSuffix_suffixNotAddedTwice() {
+ underTest.setIcon("myslot$EXTERNAL_SLOT_SUFFIX", createExternalIcon())
+
+ assertThat(iconList.slots).hasSize(1)
+ assertThat(iconList.slots[0].name).isEqualTo("myslot$EXTERNAL_SLOT_SUFFIX")
+ }
+
+ private fun createExternalIcon(): StatusBarIcon {
+ return StatusBarIcon(
+ "external.package",
+ UserHandle.ALL,
+ /* iconId= */ 2,
+ /* iconLevel= */ 0,
+ /* number= */ 0,
+ "contentDescription",
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 04a67006d686..1ba0a36fb05d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.phone;
import static com.android.systemui.flags.Flags.MODERN_BOUNCER;
+import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
+import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -37,6 +39,8 @@ import android.testing.TestableLooper;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
+import android.window.BackEvent;
+import android.window.OnBackAnimationCallback;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
import android.window.WindowOnBackInvokedDispatcher;
@@ -54,10 +58,12 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.data.BouncerView;
import com.android.systemui.keyguard.data.BouncerViewDelegate;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -118,16 +124,20 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
@Mock private BouncerView mBouncerView;
@Mock private BouncerViewDelegate mBouncerViewDelegate;
+ @Mock private OnBackAnimationCallback mBouncerViewDelegateBackCallback;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private KeyguardBouncer.PrimaryBouncerExpansionCallback mBouncerExpansionCallback;
+ private PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
+ mBouncerExpansionCallback;
private FakeKeyguardStateController mKeyguardStateController =
spy(new FakeKeyguardStateController());
- @Mock private ViewRootImpl mViewRootImpl;
- @Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
+ @Mock
+ private ViewRootImpl mViewRootImpl;
+ @Mock
+ private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
@Captor
- private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
+ private ArgumentCaptor<OnBackInvokedCallback> mBackCallbackCaptor;
@Before
@@ -138,6 +148,10 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class)))
.thenReturn(mKeyguardMessageAreaController);
when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate);
+ when(mBouncerViewDelegate.getBackCallback()).thenReturn(mBouncerViewDelegateBackCallback);
+ when(mFeatureFlags
+ .isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM))
+ .thenReturn(true);
when(mFeatureFlags.isEnabled(MODERN_BOUNCER)).thenReturn(true);
@@ -181,8 +195,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mNotificationContainer,
mBypassController);
mStatusBarKeyguardViewManager.show(null);
- ArgumentCaptor<KeyguardBouncer.PrimaryBouncerExpansionCallback> callbackArgumentCaptor =
- ArgumentCaptor.forClass(KeyguardBouncer.PrimaryBouncerExpansionCallback.class);
+ ArgumentCaptor<PrimaryBouncerExpansionCallback> callbackArgumentCaptor =
+ ArgumentCaptor.forClass(PrimaryBouncerExpansionCallback.class);
verify(mPrimaryBouncerCallbackInteractor).addBouncerExpansionCallback(
callbackArgumentCaptor.capture());
mBouncerExpansionCallback = callbackArgumentCaptor.getValue();
@@ -191,7 +205,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
public void dismissWithAction_AfterKeyguardGoneSetToFalse() {
OnDismissAction action = () -> false;
- Runnable cancelAction = () -> {};
+ Runnable cancelAction = () -> {
+ };
mStatusBarKeyguardViewManager.dismissWithAction(
action, cancelAction, false /* afterKeyguardGone */);
verify(mPrimaryBouncerInteractor).setDismissAction(eq(action), eq(cancelAction));
@@ -255,7 +270,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mPrimaryBouncerInteractor).setPanelExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
+ verify(mPrimaryBouncerInteractor).setPanelExpansion(eq(EXPANSION_HIDDEN));
}
@Test
@@ -283,7 +298,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
.thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
expansionEvent(
- /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* fraction= */ EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
verify(mPrimaryBouncerInteractor, never()).setPanelExpansion(anyFloat());
@@ -300,7 +315,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
.thenReturn(BiometricUnlockController.MODE_DISMISS_BOUNCER);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
expansionEvent(
- /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* fraction= */ EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
verify(mPrimaryBouncerInteractor, never()).setPanelExpansion(anyFloat());
@@ -311,7 +326,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
when(mKeyguardStateController.isOccluded()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
expansionEvent(
- /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* fraction= */ EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
verify(mPrimaryBouncerInteractor, never()).setPanelExpansion(anyFloat());
@@ -328,7 +343,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
.thenReturn(BiometricUnlockController.MODE_SHOW_BOUNCER);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
expansionEvent(
- /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* fraction= */ EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
verify(mPrimaryBouncerInteractor, never()).setPanelExpansion(anyFloat());
@@ -339,7 +354,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
expansionEvent(
- /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* fraction= */ EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
verify(mPrimaryBouncerInteractor, never()).setPanelExpansion(anyFloat());
@@ -539,12 +554,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mBouncerExpansionCallback.onVisibilityChanged(true);
verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
- mOnBackInvokedCallback.capture());
+ mBackCallbackCaptor.capture());
/* verify that the same callback is unregistered when the bouncer becomes invisible */
mBouncerExpansionCallback.onVisibilityChanged(false);
verify(mOnBackInvokedDispatcher).unregisterOnBackInvokedCallback(
- eq(mOnBackInvokedCallback.getValue()));
+ eq(mBackCallbackCaptor.getValue()));
}
@Test
@@ -553,18 +568,63 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
/* capture the predictive back callback during registration */
verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
- mOnBackInvokedCallback.capture());
+ mBackCallbackCaptor.capture());
when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
when(mCentralSurfaces.shouldKeyguardHideImmediately()).thenReturn(true);
/* invoke the back callback directly */
- mOnBackInvokedCallback.getValue().onBackInvoked();
+ mBackCallbackCaptor.getValue().onBackInvoked();
/* verify that the bouncer will be hidden as a result of the invocation */
verify(mCentralSurfaces).setBouncerShowing(eq(false));
}
@Test
+ public void testPredictiveBackCallback_noBackAnimationForFullScreenBouncer() {
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
+ .thenReturn(KeyguardSecurityModel.SecurityMode.SimPin);
+ mBouncerExpansionCallback.onVisibilityChanged(true);
+ /* capture the predictive back callback during registration */
+ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
+ mBackCallbackCaptor.capture());
+ assertTrue(mBackCallbackCaptor.getValue() instanceof OnBackAnimationCallback);
+
+ OnBackAnimationCallback backCallback =
+ (OnBackAnimationCallback) mBackCallbackCaptor.getValue();
+
+ BackEvent event = new BackEvent(0, 0, 0, BackEvent.EDGE_LEFT);
+ backCallback.onBackStarted(event);
+ verify(mBouncerViewDelegateBackCallback, never()).onBackStarted(any());
+ }
+
+ @Test
+ public void testPredictiveBackCallback_forwardsBackDispatches() {
+ mBouncerExpansionCallback.onVisibilityChanged(true);
+ /* capture the predictive back callback during registration */
+ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
+ mBackCallbackCaptor.capture());
+ assertTrue(mBackCallbackCaptor.getValue() instanceof OnBackAnimationCallback);
+
+ OnBackAnimationCallback backCallback =
+ (OnBackAnimationCallback) mBackCallbackCaptor.getValue();
+
+ BackEvent event = new BackEvent(0, 0, 0, BackEvent.EDGE_LEFT);
+ backCallback.onBackStarted(event);
+ verify(mBouncerViewDelegateBackCallback).onBackStarted(eq(event));
+
+ backCallback.onBackProgressed(event);
+ verify(mBouncerViewDelegateBackCallback).onBackProgressed(eq(event));
+
+ backCallback.onBackInvoked();
+ verify(mBouncerViewDelegateBackCallback).onBackInvoked();
+
+ backCallback.onBackCancelled();
+ verify(mBouncerViewDelegateBackCallback).onBackCancelled();
+ }
+
+ @Test
public void testReportBouncerOnDreamWhenVisible() {
mBouncerExpansionCallback.onVisibilityChanged(true);
verify(mCentralSurfaces).setBouncerShowingOverDream(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java
index a9c55fa06cc5..55ab6812da91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.phone;
import static com.android.systemui.flags.Flags.MODERN_BOUNCER;
+import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
+import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -58,6 +60,7 @@ import com.android.systemui.keyguard.data.BouncerView;
import com.android.systemui.keyguard.data.BouncerViewDelegate;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -124,7 +127,8 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase {
@Mock private BouncerViewDelegate mBouncerViewDelegate;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private KeyguardBouncer.PrimaryBouncerExpansionCallback mBouncerExpansionCallback;
+ private PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
+ mBouncerExpansionCallback;
private FakeKeyguardStateController mKeyguardStateController =
spy(new FakeKeyguardStateController());
@@ -139,7 +143,7 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
when(mKeyguardBouncerFactory.create(
any(ViewGroup.class),
- any(KeyguardBouncer.PrimaryBouncerExpansionCallback.class)))
+ any(PrimaryBouncerExpansionCallback.class)))
.thenReturn(mPrimaryBouncer);
when(mCentralSurfaces.getBouncerContainer()).thenReturn(mContainer);
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
@@ -187,8 +191,8 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase {
mNotificationContainer,
mBypassController);
mStatusBarKeyguardViewManager.show(null);
- ArgumentCaptor<KeyguardBouncer.PrimaryBouncerExpansionCallback> callbackArgumentCaptor =
- ArgumentCaptor.forClass(KeyguardBouncer.PrimaryBouncerExpansionCallback.class);
+ ArgumentCaptor<PrimaryBouncerExpansionCallback> callbackArgumentCaptor =
+ ArgumentCaptor.forClass(PrimaryBouncerExpansionCallback.class);
verify(mKeyguardBouncerFactory).create(any(ViewGroup.class),
callbackArgumentCaptor.capture());
mBouncerExpansionCallback = callbackArgumentCaptor.getValue();
@@ -261,7 +265,7 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase {
when(mPrimaryBouncer.inTransit()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mPrimaryBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
+ verify(mPrimaryBouncer).setExpansion(eq(EXPANSION_HIDDEN));
}
@Test
@@ -289,7 +293,7 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase {
.thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
expansionEvent(
- /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* fraction= */ EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
@@ -306,7 +310,7 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase {
.thenReturn(BiometricUnlockController.MODE_DISMISS_BOUNCER);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
expansionEvent(
- /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* fraction= */ EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
@@ -317,7 +321,7 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase {
when(mKeyguardStateController.isOccluded()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
expansionEvent(
- /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* fraction= */ EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
@@ -334,7 +338,7 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase {
.thenReturn(BiometricUnlockController.MODE_SHOW_BOUNCER);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
expansionEvent(
- /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* fraction= */ EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
@@ -345,7 +349,7 @@ public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase {
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
expansionEvent(
- /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* fraction= */ EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index 49d4bdc88c82..0add905e2750 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -26,6 +26,7 @@ import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
// TODO(b/261632894): remove this in favor of the real impl or DemoMobileConnectionsRepository
@@ -56,6 +57,10 @@ class FakeMobileConnectionsRepository(
private val _activeMobileDataSubscriptionId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
override val activeMobileDataSubscriptionId = _activeMobileDataSubscriptionId
+ override val activeSubChangedInGroupEvent: MutableSharedFlow<Unit> = MutableSharedFlow()
+
+ private val _defaultDataSubId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
+ override val defaultDataSubId = _defaultDataSubId
private val _mobileConnectivity = MutableStateFlow(MobileConnectivityModel())
override val defaultMobileNetworkConnectivity = _mobileConnectivity
@@ -81,6 +86,10 @@ class FakeMobileConnectionsRepository(
_subscriptions.value = subs
}
+ fun setDefaultDataSubId(id: Int) {
+ _defaultDataSubId.value = id
+ }
+
fun setMobileConnectivity(model: MobileConnectivityModel) {
_mobileConnectivity.value = model
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 5d377a8658a5..0859d140c3b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -34,6 +34,8 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.valid
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.kotlinArgumentCaptor
import com.android.systemui.util.mockito.mock
@@ -71,8 +73,10 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
private lateinit var underTest: MobileRepositorySwitcher
private lateinit var realRepo: MobileConnectionsRepositoryImpl
private lateinit var demoRepo: DemoMobileConnectionsRepository
- private lateinit var mockDataSource: DemoModeMobileConnectionDataSource
+ private lateinit var mobileDataSource: DemoModeMobileConnectionDataSource
+ private lateinit var wifiDataSource: DemoModeWifiDataSource
private lateinit var logFactory: TableLogBufferFactory
+ private lateinit var wifiRepository: FakeWifiRepository
@Mock private lateinit var connectivityManager: ConnectivityManager
@Mock private lateinit var subscriptionManager: SubscriptionManager
@@ -96,10 +100,15 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
// Never start in demo mode
whenever(demoModeController.isInDemoMode).thenReturn(false)
- mockDataSource =
+ mobileDataSource =
mock<DemoModeMobileConnectionDataSource>().also {
whenever(it.mobileEvents).thenReturn(fakeNetworkEventsFlow)
}
+ wifiDataSource =
+ mock<DemoModeWifiDataSource>().also {
+ whenever(it.wifiEvents).thenReturn(MutableStateFlow(null))
+ }
+ wifiRepository = FakeWifiRepository()
realRepo =
MobileConnectionsRepositoryImpl(
@@ -113,12 +122,14 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
context,
IMMEDIATE,
scope,
+ wifiRepository,
mock(),
)
demoRepo =
DemoMobileConnectionsRepository(
- dataSource = mockDataSource,
+ mobileDataSource = mobileDataSource,
+ wifiDataSource = wifiDataSource,
scope = scope,
context = context,
logFactory = logFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
index 210208532dd4..6989b514a703 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
@@ -29,6 +29,8 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectio
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
@@ -63,10 +65,12 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
private val testScope = TestScope(testDispatcher)
private val fakeNetworkEventFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
+ private val fakeWifiEventFlow = MutableStateFlow<FakeWifiEventModel?>(null)
private lateinit var connectionsRepo: DemoMobileConnectionsRepository
private lateinit var underTest: DemoMobileConnectionRepository
private lateinit var mockDataSource: DemoModeMobileConnectionDataSource
+ private lateinit var mockWifiDataSource: DemoModeWifiDataSource
@Before
fun setUp() {
@@ -75,10 +79,15 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
mock<DemoModeMobileConnectionDataSource>().also {
whenever(it.mobileEvents).thenReturn(fakeNetworkEventFlow)
}
+ mockWifiDataSource =
+ mock<DemoModeWifiDataSource>().also {
+ whenever(it.wifiEvents).thenReturn(fakeWifiEventFlow)
+ }
connectionsRepo =
DemoMobileConnectionsRepository(
- dataSource = mockDataSource,
+ mobileDataSource = mockDataSource,
+ wifiDataSource = mockWifiDataSource,
scope = testScope.backgroundScope,
context = context,
logFactory = logFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
index cdbe75e855bc..f12d113cfaa8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
@@ -32,6 +32,8 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionMod
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel.MobileDisabled
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
@@ -57,21 +59,28 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
private val testScope = TestScope(testDispatcher)
private val fakeNetworkEventFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
+ private val fakeWifiEventFlow = MutableStateFlow<FakeWifiEventModel?>(null)
private lateinit var underTest: DemoMobileConnectionsRepository
- private lateinit var mockDataSource: DemoModeMobileConnectionDataSource
+ private lateinit var mobileDataSource: DemoModeMobileConnectionDataSource
+ private lateinit var wifiDataSource: DemoModeWifiDataSource
@Before
fun setUp() {
// The data source only provides one API, so we can mock it with a flow here for convenience
- mockDataSource =
+ mobileDataSource =
mock<DemoModeMobileConnectionDataSource>().also {
whenever(it.mobileEvents).thenReturn(fakeNetworkEventFlow)
}
+ wifiDataSource =
+ mock<DemoModeWifiDataSource>().also {
+ whenever(it.wifiEvents).thenReturn(fakeWifiEventFlow)
+ }
underTest =
DemoMobileConnectionsRepository(
- dataSource = mockDataSource,
+ mobileDataSource = mobileDataSource,
+ wifiDataSource = wifiDataSource,
scope = testScope.backgroundScope,
context = context,
logFactory = logFactory,
@@ -81,6 +90,14 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
}
@Test
+ fun `connectivity - defaults to connected and validated`() =
+ testScope.runTest {
+ val connectivity = underTest.defaultMobileNetworkConnectivity.value
+ assertThat(connectivity.isConnected).isTrue()
+ assertThat(connectivity.isValidated).isTrue()
+ }
+
+ @Test
fun `network event - create new subscription`() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
@@ -97,6 +114,22 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
}
@Test
+ fun `wifi carrier merged event - create new subscription`() =
+ testScope.runTest {
+ var latest: List<SubscriptionModel>? = null
+ val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEmpty()
+
+ fakeWifiEventFlow.value = validCarrierMergedEvent(subId = 5)
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].subscriptionId).isEqualTo(5)
+
+ job.cancel()
+ }
+
+ @Test
fun `network event - reuses subscription when same Id`() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
@@ -119,6 +152,28 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
}
@Test
+ fun `wifi carrier merged event - reuses subscription when same Id`() =
+ testScope.runTest {
+ var latest: List<SubscriptionModel>? = null
+ val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEmpty()
+
+ fakeWifiEventFlow.value = validCarrierMergedEvent(subId = 5, level = 1)
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].subscriptionId).isEqualTo(5)
+
+ // Second network event comes in with the same subId, does not create a new subscription
+ fakeWifiEventFlow.value = validCarrierMergedEvent(subId = 5, level = 2)
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].subscriptionId).isEqualTo(5)
+
+ job.cancel()
+ }
+
+ @Test
fun `multiple subscriptions`() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
@@ -133,6 +188,35 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
}
@Test
+ fun `mobile subscription and carrier merged subscription`() =
+ testScope.runTest {
+ var latest: List<SubscriptionModel>? = null
+ val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
+
+ fakeNetworkEventFlow.value = validMobileEvent(subId = 1)
+ fakeWifiEventFlow.value = validCarrierMergedEvent(subId = 5)
+
+ assertThat(latest).hasSize(2)
+
+ job.cancel()
+ }
+
+ @Test
+ fun `multiple mobile subscriptions and carrier merged subscription`() =
+ testScope.runTest {
+ var latest: List<SubscriptionModel>? = null
+ val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
+
+ fakeNetworkEventFlow.value = validMobileEvent(subId = 1)
+ fakeNetworkEventFlow.value = validMobileEvent(subId = 2)
+ fakeWifiEventFlow.value = validCarrierMergedEvent(subId = 3)
+
+ assertThat(latest).hasSize(3)
+
+ job.cancel()
+ }
+
+ @Test
fun `mobile disabled event - disables connection - subId specified - single conn`() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
@@ -194,6 +278,112 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
job.cancel()
}
+ @Test
+ fun `wifi network updates to disabled - carrier merged connection removed`() =
+ testScope.runTest {
+ var latest: List<SubscriptionModel>? = null
+ val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
+
+ fakeWifiEventFlow.value = validCarrierMergedEvent(subId = 1)
+
+ assertThat(latest).hasSize(1)
+
+ fakeWifiEventFlow.value = FakeWifiEventModel.WifiDisabled
+
+ assertThat(latest).isEmpty()
+
+ job.cancel()
+ }
+
+ @Test
+ fun `wifi network updates to active - carrier merged connection removed`() =
+ testScope.runTest {
+ var latest: List<SubscriptionModel>? = null
+ val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
+
+ fakeWifiEventFlow.value = validCarrierMergedEvent(subId = 1)
+
+ assertThat(latest).hasSize(1)
+
+ fakeWifiEventFlow.value =
+ FakeWifiEventModel.Wifi(
+ level = 1,
+ activity = 0,
+ ssid = null,
+ validated = true,
+ )
+
+ assertThat(latest).isEmpty()
+
+ job.cancel()
+ }
+
+ @Test
+ fun `mobile sub updates to carrier merged - only one connection`() =
+ testScope.runTest {
+ var latestSubsList: List<SubscriptionModel>? = null
+ var connections: List<DemoMobileConnectionRepository>? = null
+ val job =
+ underTest.subscriptions
+ .onEach { latestSubsList = it }
+ .onEach { infos ->
+ connections =
+ infos.map { info -> underTest.getRepoForSubId(info.subscriptionId) }
+ }
+ .launchIn(this)
+
+ fakeNetworkEventFlow.value = validMobileEvent(subId = 3, level = 2)
+ assertThat(latestSubsList).hasSize(1)
+
+ val carrierMergedEvent = validCarrierMergedEvent(subId = 3, level = 1)
+ fakeWifiEventFlow.value = carrierMergedEvent
+ assertThat(latestSubsList).hasSize(1)
+ val connection = connections!!.find { it.subId == 3 }!!
+ assertCarrierMergedConnection(connection, carrierMergedEvent)
+
+ job.cancel()
+ }
+
+ @Test
+ fun `mobile sub updates to carrier merged then back - has old mobile data`() =
+ testScope.runTest {
+ var latestSubsList: List<SubscriptionModel>? = null
+ var connections: List<DemoMobileConnectionRepository>? = null
+ val job =
+ underTest.subscriptions
+ .onEach { latestSubsList = it }
+ .onEach { infos ->
+ connections =
+ infos.map { info -> underTest.getRepoForSubId(info.subscriptionId) }
+ }
+ .launchIn(this)
+
+ val mobileEvent = validMobileEvent(subId = 3, level = 2)
+ fakeNetworkEventFlow.value = mobileEvent
+ assertThat(latestSubsList).hasSize(1)
+
+ val carrierMergedEvent = validCarrierMergedEvent(subId = 3, level = 1)
+ fakeWifiEventFlow.value = carrierMergedEvent
+ assertThat(latestSubsList).hasSize(1)
+ var connection = connections!!.find { it.subId == 3 }!!
+ assertCarrierMergedConnection(connection, carrierMergedEvent)
+
+ // WHEN the carrier merged is removed
+ fakeWifiEventFlow.value =
+ FakeWifiEventModel.Wifi(
+ level = 4,
+ activity = 0,
+ ssid = null,
+ validated = true,
+ )
+
+ // THEN the subId=3 connection goes back to the mobile information
+ connection = connections!!.find { it.subId == 3 }!!
+ assertConnection(connection, mobileEvent)
+
+ job.cancel()
+ }
+
/** Regression test for b/261706421 */
@Test
fun `multiple connections - remove all - does not throw`() =
@@ -289,6 +479,51 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
job.cancel()
}
+ @Test
+ fun `demo connection - two connections - update carrier merged - no affect on first`() =
+ testScope.runTest {
+ var currentEvent1 = validMobileEvent(subId = 1)
+ var connection1: DemoMobileConnectionRepository? = null
+ var currentEvent2 = validCarrierMergedEvent(subId = 2)
+ var connection2: DemoMobileConnectionRepository? = null
+ var connections: List<DemoMobileConnectionRepository>? = null
+ val job =
+ underTest.subscriptions
+ .onEach { infos ->
+ connections =
+ infos.map { info -> underTest.getRepoForSubId(info.subscriptionId) }
+ }
+ .launchIn(this)
+
+ fakeNetworkEventFlow.value = currentEvent1
+ fakeWifiEventFlow.value = currentEvent2
+ assertThat(connections).hasSize(2)
+ connections!!.forEach {
+ when (it.subId) {
+ 1 -> connection1 = it
+ 2 -> connection2 = it
+ else -> Assert.fail("Unexpected subscription")
+ }
+ }
+
+ assertConnection(connection1!!, currentEvent1)
+ assertCarrierMergedConnection(connection2!!, currentEvent2)
+
+ // WHEN the event changes for connection 2, it updates, and connection 1 stays the same
+ currentEvent2 = validCarrierMergedEvent(subId = 2, level = 4)
+ fakeWifiEventFlow.value = currentEvent2
+ assertConnection(connection1!!, currentEvent1)
+ assertCarrierMergedConnection(connection2!!, currentEvent2)
+
+ // and vice versa
+ currentEvent1 = validMobileEvent(subId = 1, inflateStrength = true)
+ fakeNetworkEventFlow.value = currentEvent1
+ assertConnection(connection1!!, currentEvent1)
+ assertCarrierMergedConnection(connection2!!, currentEvent2)
+
+ job.cancel()
+ }
+
private fun assertConnection(
conn: DemoMobileConnectionRepository,
model: FakeNetworkEventModel
@@ -315,6 +550,21 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
else -> {}
}
}
+
+ private fun assertCarrierMergedConnection(
+ conn: DemoMobileConnectionRepository,
+ model: FakeWifiEventModel.CarrierMerged,
+ ) {
+ val connectionInfo: MobileConnectionModel = conn.connectionInfo.value
+ assertThat(conn.subId).isEqualTo(model.subscriptionId)
+ assertThat(connectionInfo.cdmaLevel).isEqualTo(model.level)
+ assertThat(connectionInfo.primaryLevel).isEqualTo(model.level)
+ assertThat(connectionInfo.carrierNetworkChangeActive).isEqualTo(false)
+ assertThat(connectionInfo.isRoaming).isEqualTo(false)
+ assertThat(connectionInfo.isEmergencyOnly).isFalse()
+ assertThat(connectionInfo.isGsm).isFalse()
+ assertThat(connectionInfo.dataConnectionState).isEqualTo(DataConnectionState.Connected)
+ }
}
/** Convenience to create a valid fake network event with minimal params */
@@ -339,3 +589,14 @@ fun validMobileEvent(
roaming = roaming,
name = "demo name",
)
+
+fun validCarrierMergedEvent(
+ subId: Int = 1,
+ level: Int = 1,
+ numberOfLevels: Int = 4,
+): FakeWifiEventModel.CarrierMerged =
+ FakeWifiEventModel.CarrierMerged(
+ subscriptionId = subId,
+ level = level,
+ numberOfLevels = numberOfLevels,
+ )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
new file mode 100644
index 000000000000..ea90150b432a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
+import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
+import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidTestingRunner::class)
+class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
+
+ private lateinit var underTest: CarrierMergedConnectionRepository
+
+ private lateinit var wifiRepository: FakeWifiRepository
+ @Mock private lateinit var logger: TableLogBuffer
+
+ private val testDispatcher = UnconfinedTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ wifiRepository = FakeWifiRepository()
+
+ underTest =
+ CarrierMergedConnectionRepository(
+ SUB_ID,
+ logger,
+ NetworkNameModel.Default("name"),
+ testScope.backgroundScope,
+ wifiRepository,
+ )
+ }
+
+ @Test
+ fun connectionInfo_inactiveWifi_isDefault() =
+ testScope.runTest {
+ var latest: MobileConnectionModel? = null
+ val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
+
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+
+ assertThat(latest).isEqualTo(MobileConnectionModel())
+
+ job.cancel()
+ }
+
+ @Test
+ fun connectionInfo_activeWifi_isDefault() =
+ testScope.runTest {
+ var latest: MobileConnectionModel? = null
+ val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
+
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = NET_ID, level = 1))
+
+ assertThat(latest).isEqualTo(MobileConnectionModel())
+
+ job.cancel()
+ }
+
+ @Test
+ fun connectionInfo_carrierMergedWifi_isValidAndFieldsComeFromWifiNetwork() =
+ testScope.runTest {
+ var latest: MobileConnectionModel? = null
+ val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
+
+ wifiRepository.setIsWifiEnabled(true)
+ wifiRepository.setIsWifiDefault(true)
+
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.CarrierMerged(
+ networkId = NET_ID,
+ subscriptionId = SUB_ID,
+ level = 3,
+ )
+ )
+
+ val expected =
+ MobileConnectionModel(
+ primaryLevel = 3,
+ cdmaLevel = 3,
+ dataConnectionState = DataConnectionState.Connected,
+ dataActivityDirection =
+ DataActivityModel(
+ hasActivityIn = false,
+ hasActivityOut = false,
+ ),
+ resolvedNetworkType = ResolvedNetworkType.CarrierMergedNetworkType,
+ isRoaming = false,
+ isEmergencyOnly = false,
+ operatorAlphaShort = null,
+ isInService = true,
+ isGsm = false,
+ carrierNetworkChangeActive = false,
+ )
+ assertThat(latest).isEqualTo(expected)
+
+ job.cancel()
+ }
+
+ @Test
+ fun connectionInfo_carrierMergedWifi_wrongSubId_isDefault() =
+ testScope.runTest {
+ var latest: MobileConnectionModel? = null
+ val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
+
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.CarrierMerged(
+ networkId = NET_ID,
+ subscriptionId = SUB_ID + 10,
+ level = 3,
+ )
+ )
+
+ assertThat(latest).isEqualTo(MobileConnectionModel())
+ assertThat(latest!!.primaryLevel).isNotEqualTo(3)
+ assertThat(latest!!.resolvedNetworkType)
+ .isNotEqualTo(ResolvedNetworkType.CarrierMergedNetworkType)
+
+ job.cancel()
+ }
+
+ // This scenario likely isn't possible, but write a test for it anyway
+ @Test
+ fun connectionInfo_carrierMergedButNotEnabled_isDefault() =
+ testScope.runTest {
+ var latest: MobileConnectionModel? = null
+ val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
+
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.CarrierMerged(
+ networkId = NET_ID,
+ subscriptionId = SUB_ID,
+ level = 3,
+ )
+ )
+ wifiRepository.setIsWifiEnabled(false)
+
+ assertThat(latest).isEqualTo(MobileConnectionModel())
+
+ job.cancel()
+ }
+
+ // This scenario likely isn't possible, but write a test for it anyway
+ @Test
+ fun connectionInfo_carrierMergedButWifiNotDefault_isDefault() =
+ testScope.runTest {
+ var latest: MobileConnectionModel? = null
+ val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
+
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.CarrierMerged(
+ networkId = NET_ID,
+ subscriptionId = SUB_ID,
+ level = 3,
+ )
+ )
+ wifiRepository.setIsWifiDefault(false)
+
+ assertThat(latest).isEqualTo(MobileConnectionModel())
+
+ job.cancel()
+ }
+
+ @Test
+ fun numberOfLevels_comesFromCarrierMerged() =
+ testScope.runTest {
+ var latest: Int? = null
+ val job = underTest.numberOfLevels.onEach { latest = it }.launchIn(this)
+
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.CarrierMerged(
+ networkId = NET_ID,
+ subscriptionId = SUB_ID,
+ level = 1,
+ numberOfLevels = 6,
+ )
+ )
+
+ assertThat(latest).isEqualTo(6)
+
+ job.cancel()
+ }
+
+ @Test
+ fun dataEnabled_matchesWifiEnabled() =
+ testScope.runTest {
+ var latest: Boolean? = null
+ val job = underTest.dataEnabled.onEach { latest = it }.launchIn(this)
+
+ wifiRepository.setIsWifiEnabled(true)
+ assertThat(latest).isTrue()
+
+ wifiRepository.setIsWifiEnabled(false)
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun cdmaRoaming_alwaysFalse() =
+ testScope.runTest {
+ var latest: Boolean? = null
+ val job = underTest.cdmaRoaming.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ private companion object {
+ const val SUB_ID = 123
+ const val NET_ID = 456
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
new file mode 100644
index 000000000000..c02a4dfd074c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.pipeline.mobile.data.repository.prod
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+/**
+ * This repo acts as a dispatcher to either the `typical` or `carrier merged` versions of the
+ * repository interface it's switching on. These tests just need to verify that the entire interface
+ * properly switches over when the value of `isCarrierMerged` changes.
+ */
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+class FullMobileConnectionRepositoryTest : SysuiTestCase() {
+ private lateinit var underTest: FullMobileConnectionRepository
+
+ private val testDispatcher = UnconfinedTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+ private val mobileMappings = FakeMobileMappingsProxy()
+ private val tableLogBuffer = mock<TableLogBuffer>()
+ private val mobileFactory = mock<MobileConnectionRepositoryImpl.Factory>()
+ private val carrierMergedFactory = mock<CarrierMergedConnectionRepository.Factory>()
+
+ private lateinit var connectionsRepo: FakeMobileConnectionsRepository
+ private val globalMobileDataSettingChangedEvent: Flow<Unit>
+ get() = connectionsRepo.globalMobileDataSettingChangedEvent
+
+ private lateinit var mobileRepo: FakeMobileConnectionRepository
+ private lateinit var carrierMergedRepo: FakeMobileConnectionRepository
+
+ @Before
+ fun setUp() {
+ connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogBuffer)
+
+ mobileRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer)
+ carrierMergedRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer)
+
+ whenever(
+ mobileFactory.build(
+ eq(SUB_ID),
+ any(),
+ eq(DEFAULT_NAME),
+ eq(SEP),
+ eq(globalMobileDataSettingChangedEvent),
+ )
+ )
+ .thenReturn(mobileRepo)
+ whenever(carrierMergedFactory.build(eq(SUB_ID), any(), eq(DEFAULT_NAME)))
+ .thenReturn(carrierMergedRepo)
+ }
+
+ @Test
+ fun startingIsCarrierMerged_usesCarrierMergedInitially() =
+ testScope.runTest {
+ val carrierMergedConnectionInfo =
+ MobileConnectionModel(
+ operatorAlphaShort = "Carrier Merged Operator",
+ )
+ carrierMergedRepo.setConnectionInfo(carrierMergedConnectionInfo)
+
+ initializeRepo(startingIsCarrierMerged = true)
+
+ assertThat(underTest.activeRepo.value).isEqualTo(carrierMergedRepo)
+ assertThat(underTest.connectionInfo.value).isEqualTo(carrierMergedConnectionInfo)
+ verify(mobileFactory, never())
+ .build(
+ SUB_ID,
+ tableLogBuffer,
+ DEFAULT_NAME,
+ SEP,
+ globalMobileDataSettingChangedEvent
+ )
+ }
+
+ @Test
+ fun startingNotCarrierMerged_usesTypicalInitially() =
+ testScope.runTest {
+ val mobileConnectionInfo =
+ MobileConnectionModel(
+ operatorAlphaShort = "Typical Operator",
+ )
+ mobileRepo.setConnectionInfo(mobileConnectionInfo)
+
+ initializeRepo(startingIsCarrierMerged = false)
+
+ assertThat(underTest.activeRepo.value).isEqualTo(mobileRepo)
+ assertThat(underTest.connectionInfo.value).isEqualTo(mobileConnectionInfo)
+ verify(carrierMergedFactory, never()).build(SUB_ID, tableLogBuffer, DEFAULT_NAME)
+ }
+
+ @Test
+ fun activeRepo_matchesIsCarrierMerged() =
+ testScope.runTest {
+ initializeRepo(startingIsCarrierMerged = false)
+ var latest: MobileConnectionRepository? = null
+ val job = underTest.activeRepo.onEach { latest = it }.launchIn(this)
+
+ underTest.setIsCarrierMerged(true)
+
+ assertThat(latest).isEqualTo(carrierMergedRepo)
+
+ underTest.setIsCarrierMerged(false)
+
+ assertThat(latest).isEqualTo(mobileRepo)
+
+ underTest.setIsCarrierMerged(true)
+
+ assertThat(latest).isEqualTo(carrierMergedRepo)
+
+ job.cancel()
+ }
+
+ @Test
+ fun connectionInfo_getsUpdatesFromRepo_carrierMerged() =
+ testScope.runTest {
+ initializeRepo(startingIsCarrierMerged = false)
+
+ var latest: MobileConnectionModel? = null
+ val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
+
+ underTest.setIsCarrierMerged(true)
+
+ val info1 =
+ MobileConnectionModel(
+ operatorAlphaShort = "Carrier Merged Operator",
+ primaryLevel = 1,
+ )
+ carrierMergedRepo.setConnectionInfo(info1)
+
+ assertThat(latest).isEqualTo(info1)
+
+ val info2 =
+ MobileConnectionModel(
+ operatorAlphaShort = "Carrier Merged Operator #2",
+ primaryLevel = 2,
+ )
+ carrierMergedRepo.setConnectionInfo(info2)
+
+ assertThat(latest).isEqualTo(info2)
+
+ val info3 =
+ MobileConnectionModel(
+ operatorAlphaShort = "Carrier Merged Operator #3",
+ primaryLevel = 3,
+ )
+ carrierMergedRepo.setConnectionInfo(info3)
+
+ assertThat(latest).isEqualTo(info3)
+
+ job.cancel()
+ }
+
+ @Test
+ fun connectionInfo_getsUpdatesFromRepo_mobile() =
+ testScope.runTest {
+ initializeRepo(startingIsCarrierMerged = false)
+
+ var latest: MobileConnectionModel? = null
+ val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
+
+ underTest.setIsCarrierMerged(false)
+
+ val info1 =
+ MobileConnectionModel(
+ operatorAlphaShort = "Typical Merged Operator",
+ primaryLevel = 1,
+ )
+ mobileRepo.setConnectionInfo(info1)
+
+ assertThat(latest).isEqualTo(info1)
+
+ val info2 =
+ MobileConnectionModel(
+ operatorAlphaShort = "Typical Merged Operator #2",
+ primaryLevel = 2,
+ )
+ mobileRepo.setConnectionInfo(info2)
+
+ assertThat(latest).isEqualTo(info2)
+
+ val info3 =
+ MobileConnectionModel(
+ operatorAlphaShort = "Typical Merged Operator #3",
+ primaryLevel = 3,
+ )
+ mobileRepo.setConnectionInfo(info3)
+
+ assertThat(latest).isEqualTo(info3)
+
+ job.cancel()
+ }
+
+ @Test
+ fun connectionInfo_updatesWhenCarrierMergedUpdates() =
+ testScope.runTest {
+ initializeRepo(startingIsCarrierMerged = false)
+
+ var latest: MobileConnectionModel? = null
+ val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
+
+ val carrierMergedInfo =
+ MobileConnectionModel(
+ operatorAlphaShort = "Carrier Merged Operator",
+ primaryLevel = 4,
+ )
+ carrierMergedRepo.setConnectionInfo(carrierMergedInfo)
+
+ val mobileInfo =
+ MobileConnectionModel(
+ operatorAlphaShort = "Typical Operator",
+ primaryLevel = 2,
+ )
+ mobileRepo.setConnectionInfo(mobileInfo)
+
+ // Start with the mobile info
+ assertThat(latest).isEqualTo(mobileInfo)
+
+ // WHEN isCarrierMerged is set to true
+ underTest.setIsCarrierMerged(true)
+
+ // THEN the carrier merged info is used
+ assertThat(latest).isEqualTo(carrierMergedInfo)
+
+ val newCarrierMergedInfo =
+ MobileConnectionModel(
+ operatorAlphaShort = "New CM Operator",
+ primaryLevel = 0,
+ )
+ carrierMergedRepo.setConnectionInfo(newCarrierMergedInfo)
+
+ assertThat(latest).isEqualTo(newCarrierMergedInfo)
+
+ // WHEN isCarrierMerged is set to false
+ underTest.setIsCarrierMerged(false)
+
+ // THEN the typical info is used
+ assertThat(latest).isEqualTo(mobileInfo)
+
+ val newMobileInfo =
+ MobileConnectionModel(
+ operatorAlphaShort = "New Mobile Operator",
+ primaryLevel = 3,
+ )
+ mobileRepo.setConnectionInfo(newMobileInfo)
+
+ assertThat(latest).isEqualTo(newMobileInfo)
+
+ job.cancel()
+ }
+
+ @Test
+ fun `factory - reuses log buffers for same connection`() =
+ testScope.runTest {
+ val realLoggerFactory = TableLogBufferFactory(mock(), FakeSystemClock())
+
+ val factory =
+ FullMobileConnectionRepository.Factory(
+ scope = testScope.backgroundScope,
+ realLoggerFactory,
+ mobileFactory,
+ carrierMergedFactory,
+ )
+
+ // Create two connections for the same subId. Similar to if the connection appeared
+ // and disappeared from the connectionFactory's perspective
+ val connection1 =
+ factory.build(
+ SUB_ID,
+ startingIsCarrierMerged = false,
+ DEFAULT_NAME,
+ SEP,
+ globalMobileDataSettingChangedEvent,
+ )
+
+ val connection1Repeat =
+ factory.build(
+ SUB_ID,
+ startingIsCarrierMerged = false,
+ DEFAULT_NAME,
+ SEP,
+ globalMobileDataSettingChangedEvent,
+ )
+
+ assertThat(connection1.tableLogBuffer)
+ .isSameInstanceAs(connection1Repeat.tableLogBuffer)
+ }
+
+ @Test
+ fun `factory - reuses log buffers for same sub ID even if carrier merged`() =
+ testScope.runTest {
+ val realLoggerFactory = TableLogBufferFactory(mock(), FakeSystemClock())
+
+ val factory =
+ FullMobileConnectionRepository.Factory(
+ scope = testScope.backgroundScope,
+ realLoggerFactory,
+ mobileFactory,
+ carrierMergedFactory,
+ )
+
+ val connection1 =
+ factory.build(
+ SUB_ID,
+ startingIsCarrierMerged = false,
+ DEFAULT_NAME,
+ SEP,
+ globalMobileDataSettingChangedEvent,
+ )
+
+ // WHEN a connection with the same sub ID but carrierMerged = true is created
+ val connection1Repeat =
+ factory.build(
+ SUB_ID,
+ startingIsCarrierMerged = true,
+ DEFAULT_NAME,
+ SEP,
+ globalMobileDataSettingChangedEvent,
+ )
+
+ // THEN the same table is re-used
+ assertThat(connection1.tableLogBuffer)
+ .isSameInstanceAs(connection1Repeat.tableLogBuffer)
+ }
+
+ // TODO(b/238425913): Verify that the logging switches correctly (once the carrier merged repo
+ // implements logging).
+
+ private fun initializeRepo(startingIsCarrierMerged: Boolean) {
+ underTest =
+ FullMobileConnectionRepository(
+ SUB_ID,
+ startingIsCarrierMerged,
+ tableLogBuffer,
+ DEFAULT_NAME,
+ SEP,
+ globalMobileDataSettingChangedEvent,
+ testScope.backgroundScope,
+ mobileFactory,
+ carrierMergedFactory,
+ )
+ }
+
+ private companion object {
+ const val SUB_ID = 42
+ private val DEFAULT_NAME = NetworkNameModel.Default("default name")
+ private const val SEP = "-"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 09589707b331..ae390a0e2959 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -37,17 +37,18 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
-import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -74,6 +75,9 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
private lateinit var underTest: MobileConnectionsRepositoryImpl
private lateinit var connectionFactory: MobileConnectionRepositoryImpl.Factory
+ private lateinit var carrierMergedFactory: CarrierMergedConnectionRepository.Factory
+ private lateinit var fullConnectionFactory: FullMobileConnectionRepository.Factory
+ private lateinit var wifiRepository: FakeWifiRepository
@Mock private lateinit var connectivityManager: ConnectivityManager
@Mock private lateinit var subscriptionManager: SubscriptionManager
@Mock private lateinit var telephonyManager: TelephonyManager
@@ -100,6 +104,8 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
mock<TableLogBuffer>()
}
+ wifiRepository = FakeWifiRepository()
+
connectionFactory =
MobileConnectionRepositoryImpl.Factory(
fakeBroadcastDispatcher,
@@ -110,7 +116,18 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
logger = logger,
mobileMappingsProxy = mobileMappings,
scope = scope,
+ )
+ carrierMergedFactory =
+ CarrierMergedConnectionRepository.Factory(
+ scope,
+ wifiRepository,
+ )
+ fullConnectionFactory =
+ FullMobileConnectionRepository.Factory(
+ scope = scope,
logFactory = logBufferFactory,
+ mobileRepoFactory = connectionFactory,
+ carrierMergedRepoFactory = carrierMergedFactory,
)
underTest =
@@ -125,7 +142,8 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
context,
IMMEDIATE,
scope,
- connectionFactory,
+ wifiRepository,
+ fullConnectionFactory,
)
}
@@ -180,6 +198,40 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
}
@Test
+ fun testSubscriptions_carrierMergedOnly_listHasCarrierMerged() =
+ runBlocking(IMMEDIATE) {
+ var latest: List<SubscriptionModel>? = null
+
+ val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
+
+ wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ assertThat(latest).isEqualTo(listOf(MODEL_CM))
+
+ job.cancel()
+ }
+
+ @Test
+ fun testSubscriptions_carrierMergedAndOther_listHasBothWithCarrierMergedLast() =
+ runBlocking(IMMEDIATE) {
+ var latest: List<SubscriptionModel>? = null
+
+ val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
+
+ wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_2, SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ assertThat(latest).isEqualTo(listOf(MODEL_1, MODEL_2, MODEL_CM))
+
+ job.cancel()
+ }
+
+ @Test
fun testActiveDataSubscriptionId_initialValueIsInvalidId() =
runBlocking(IMMEDIATE) {
assertThat(underTest.activeMobileDataSubscriptionId.value)
@@ -219,6 +271,96 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
}
@Test
+ fun testConnectionRepository_carrierMergedSubId_isCached() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.subscriptions.launchIn(this)
+
+ wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val repo1 = underTest.getRepoForSubId(SUB_CM_ID)
+ val repo2 = underTest.getRepoForSubId(SUB_CM_ID)
+
+ assertThat(repo1).isSameInstanceAs(repo2)
+
+ job.cancel()
+ }
+
+ @Test
+ fun testConnectionRepository_carrierMergedAndMobileSubs_usesCorrectRepos() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.subscriptions.launchIn(this)
+
+ wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ val mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun testSubscriptions_subNoLongerCarrierMerged_repoUpdates() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.subscriptions.launchIn(this)
+
+ wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ var mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+
+ // WHEN the wifi network updates to be not carrier merged
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 4, level = 1))
+
+ // THEN the repos update
+ val noLongerCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(noLongerCarrierMergedRepo.getIsCarrierMerged()).isFalse()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun testSubscriptions_subBecomesCarrierMerged_repoUpdates() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.subscriptions.launchIn(this)
+
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val notYetCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ var mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(notYetCarrierMergedRepo.getIsCarrierMerged()).isFalse()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+
+ // WHEN the wifi network updates to be carrier merged
+ wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
+
+ // THEN the repos update
+ val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
fun testConnectionCache_clearsInvalidSubscriptions() =
runBlocking(IMMEDIATE) {
val job = underTest.subscriptions.launchIn(this)
@@ -244,6 +386,34 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
job.cancel()
}
+ @Test
+ fun testConnectionCache_clearsInvalidSubscriptions_includingCarrierMerged() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.subscriptions.launchIn(this)
+
+ wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_2, SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // Get repos to trigger caching
+ val repo1 = underTest.getRepoForSubId(SUB_1_ID)
+ val repo2 = underTest.getRepoForSubId(SUB_2_ID)
+ val repoCarrierMerged = underTest.getRepoForSubId(SUB_CM_ID)
+
+ assertThat(underTest.getSubIdRepoCache())
+ .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2, SUB_CM_ID, repoCarrierMerged)
+
+ // SUB_2 and SUB_CM disappear
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1)
+
+ job.cancel()
+ }
+
/** Regression test for b/261706421 */
@Test
fun testConnectionsCache_clearMultipleSubscriptionsAtOnce_doesNotThrow() =
@@ -295,13 +465,13 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
underTest.getRepoForSubId(SUB_1_ID)
verify(logBufferFactory)
.getOrCreate(
- eq(MobileConnectionRepositoryImpl.tableBufferLogName(SUB_1_ID)),
+ eq(tableBufferLogName(SUB_1_ID)),
anyInt(),
)
underTest.getRepoForSubId(SUB_2_ID)
verify(logBufferFactory)
.getOrCreate(
- eq(MobileConnectionRepositoryImpl.tableBufferLogName(SUB_2_ID)),
+ eq(tableBufferLogName(SUB_2_ID)),
anyInt(),
)
@@ -309,43 +479,32 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
}
@Test
- fun `connection repository factory - reuses log buffers for same connection`() =
+ fun testDefaultDataSubId_updatesOnBroadcast() =
runBlocking(IMMEDIATE) {
- val realLoggerFactory = TableLogBufferFactory(mock(), FakeSystemClock())
+ var latest: Int? = null
+ val job = underTest.defaultDataSubId.onEach { latest = it }.launchIn(this)
- connectionFactory =
- MobileConnectionRepositoryImpl.Factory(
- fakeBroadcastDispatcher,
- context = context,
- telephonyManager = telephonyManager,
- bgDispatcher = IMMEDIATE,
- globalSettings = globalSettings,
- logger = logger,
- mobileMappingsProxy = mobileMappings,
- scope = scope,
- logFactory = realLoggerFactory,
+ fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
+ receiver.onReceive(
+ context,
+ Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
+ .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_2_ID)
)
+ }
- // Create two connections for the same subId. Similar to if the connection appeared
- // and disappeared from the connectionFactory's perspective
- val connection1 =
- connectionFactory.build(
- 1,
- NetworkNameModel.Default("default_name"),
- "-",
- underTest.globalMobileDataSettingChangedEvent,
- )
+ assertThat(latest).isEqualTo(SUB_2_ID)
- val connection1_repeat =
- connectionFactory.build(
- 1,
- NetworkNameModel.Default("default_name"),
- "-",
- underTest.globalMobileDataSettingChangedEvent,
+ fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
+ receiver.onReceive(
+ context,
+ Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
+ .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_1_ID)
)
+ }
+
+ assertThat(latest).isEqualTo(SUB_1_ID)
- assertThat(connection1.tableLogBuffer)
- .isSameInstanceAs(connection1_repeat.tableLogBuffer)
+ job.cancel()
}
@Test
@@ -461,7 +620,8 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
context,
IMMEDIATE,
scope,
- connectionFactory,
+ wifiRepository,
+ fullConnectionFactory,
)
var latest: MobileMappings.Config? = null
@@ -571,5 +731,16 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
private const val NET_ID = 123
private val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) }
+
+ private const val SUB_CM_ID = 5
+ private val SUB_CM =
+ mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_CM_ID) }
+ private val MODEL_CM = SubscriptionModel(subscriptionId = SUB_CM_ID)
+ private val WIFI_NETWORK_CM =
+ WifiNetworkModel.CarrierMerged(
+ networkId = 3,
+ subscriptionId = SUB_CM_ID,
+ level = 1,
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
index a29146b01668..7aeaa48165aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
@@ -40,6 +40,8 @@ class FakeMobileIconInteractor(
)
)
+ override val isConnected = MutableStateFlow(true)
+
private val _iconGroup = MutableStateFlow<SignalIcon.MobileIconGroup>(TelephonyIcons.THREE_G)
override val networkTypeIconGroup = _iconGroup
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 1c0064610c52..172755cb8d61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -23,6 +23,7 @@ import android.telephony.TelephonyManager.NETWORK_TYPE_UMTS
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import kotlinx.coroutines.flow.MutableStateFlow
@@ -59,6 +60,9 @@ class FakeMobileIconsInteractor(
override val alwaysShowDataRatIcon = MutableStateFlow(false)
override val alwaysUseCdmaLevel = MutableStateFlow(false)
+ override val defaultDataSubId = MutableStateFlow(DEFAULT_DATA_SUB_ID)
+
+ override val defaultMobileNetworkConnectivity = MutableStateFlow(MobileConnectivityModel())
private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING)
override val defaultMobileIconMapping = _defaultMobileIconMapping
@@ -77,6 +81,8 @@ class FakeMobileIconsInteractor(
companion object {
val DEFAULT_ICON = TelephonyIcons.G
+ const val DEFAULT_DATA_SUB_ID = 1
+
// Use [MobileMappings] to define some simple definitions
const val THREE_G = NETWORK_TYPE_GSM
const val LTE = NETWORK_TYPE_LTE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index 61e13b85db6c..c42aba5a7dd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -25,6 +25,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.CarrierMergedNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
@@ -60,8 +61,10 @@ class MobileIconInteractorTest : SysuiTestCase() {
mobileIconsInteractor.activeDataConnectionHasDataEnabled,
mobileIconsInteractor.alwaysShowDataRatIcon,
mobileIconsInteractor.alwaysUseCdmaLevel,
+ mobileIconsInteractor.defaultMobileNetworkConnectivity,
mobileIconsInteractor.defaultMobileIconMapping,
mobileIconsInteractor.defaultMobileIconGroup,
+ mobileIconsInteractor.defaultDataSubId,
mobileIconsInteractor.isDefaultConnectionFailed,
connectionRepository,
)
@@ -271,6 +274,47 @@ class MobileIconInteractorTest : SysuiTestCase() {
}
@Test
+ fun iconGroup_carrierMerged_usesOverride() =
+ runBlocking(IMMEDIATE) {
+ connectionRepository.setConnectionInfo(
+ MobileConnectionModel(
+ resolvedNetworkType = CarrierMergedNetworkType,
+ ),
+ )
+
+ var latest: MobileIconGroup? = null
+ val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEqualTo(CarrierMergedNetworkType.iconGroupOverride)
+
+ job.cancel()
+ }
+
+ @Test
+ fun `icon group - checks default data`() =
+ runBlocking(IMMEDIATE) {
+ mobileIconsInteractor.defaultDataSubId.value = SUB_1_ID
+ connectionRepository.setConnectionInfo(
+ MobileConnectionModel(
+ resolvedNetworkType = DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G))
+ ),
+ )
+
+ var latest: MobileIconGroup? = null
+ val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEqualTo(TelephonyIcons.THREE_G)
+
+ // Default data sub id changes to something else
+ mobileIconsInteractor.defaultDataSubId.value = 123
+ yield()
+
+ assertThat(latest).isEqualTo(TelephonyIcons.NOT_DEFAULT_DATA)
+
+ job.cancel()
+ }
+
+ @Test
fun alwaysShowDataRatIcon_matchesParent() =
runBlocking(IMMEDIATE) {
var latest: Boolean? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index b82a58414db9..1b62d5cc15b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -31,11 +31,13 @@ import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.yield
import org.junit.After
import org.junit.Before
@@ -43,13 +45,16 @@ import org.junit.Test
import org.mockito.Mock
import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class MobileIconsInteractorTest : SysuiTestCase() {
private lateinit var underTest: MobileIconsInteractor
private lateinit var connectionsRepository: FakeMobileConnectionsRepository
private val userSetupRepository = FakeUserSetupRepository()
private val mobileMappingsProxy = FakeMobileMappingsProxy()
- private val scope = CoroutineScope(IMMEDIATE)
+
+ private val testDispatcher = UnconfinedTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
@Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
@@ -73,7 +78,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
connectionsRepository,
carrierConfigTracker,
userSetupRepository,
- scope
+ testScope.backgroundScope,
)
}
@@ -81,7 +86,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun filteredSubscriptions_default() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
@@ -92,7 +97,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun filteredSubscriptions_nonOpportunistic_updatesWithMultipleSubs() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
var latest: List<SubscriptionModel>? = null
@@ -105,7 +110,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_3() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
@@ -122,7 +127,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_4() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
@@ -139,7 +144,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_active_1() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
@@ -157,7 +162,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_nonActive_1() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
@@ -175,7 +180,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun activeDataConnection_turnedOn() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
CONNECTION_1.setDataEnabled(true)
var latest: Boolean? = null
val job =
@@ -188,7 +193,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun activeDataConnection_turnedOff() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
CONNECTION_1.setDataEnabled(true)
var latest: Boolean? = null
val job =
@@ -204,7 +209,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun activeDataConnection_invalidSubId() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job =
underTest.activeDataConnectionHasDataEnabled.onEach { latest = it }.launchIn(this)
@@ -220,7 +225,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun failedConnection_connected_validated_notFailed() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
connectionsRepository.setMobileConnectivity(MobileConnectivityModel(true, true))
@@ -233,7 +238,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun failedConnection_notConnected_notValidated_notFailed() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
@@ -247,7 +252,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun failedConnection_connected_notValidated_failed() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
@@ -261,7 +266,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun alwaysShowDataRatIcon_configHasTrue() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this)
@@ -277,7 +282,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun alwaysShowDataRatIcon_configHasFalse() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this)
@@ -293,7 +298,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun alwaysUseCdmaLevel_configHasTrue() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
@@ -309,7 +314,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun alwaysUseCdmaLevel_configHasFalse() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
@@ -323,8 +328,286 @@ class MobileIconsInteractorTest : SysuiTestCase() {
job.cancel()
}
+ @Test
+ fun `default mobile connectivity - uses repo value`() =
+ testScope.runTest {
+ var latest: MobileConnectivityModel? = null
+ val job =
+ underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
+
+ var expected = MobileConnectivityModel(isConnected = true, isValidated = true)
+ connectionsRepository.setMobileConnectivity(expected)
+ assertThat(latest).isEqualTo(expected)
+
+ expected = MobileConnectivityModel(isConnected = false, isValidated = true)
+ connectionsRepository.setMobileConnectivity(expected)
+ assertThat(latest).isEqualTo(expected)
+
+ expected = MobileConnectivityModel(isConnected = true, isValidated = false)
+ connectionsRepository.setMobileConnectivity(expected)
+ assertThat(latest).isEqualTo(expected)
+
+ expected = MobileConnectivityModel(isConnected = false, isValidated = false)
+ connectionsRepository.setMobileConnectivity(expected)
+ assertThat(latest).isEqualTo(expected)
+
+ job.cancel()
+ }
+
+ @Test
+ fun `data switch - in same group - validated matches previous value`() =
+ testScope.runTest {
+ var latest: MobileConnectivityModel? = null
+ val job =
+ underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
+
+ connectionsRepository.setMobileConnectivity(
+ MobileConnectivityModel(
+ isConnected = true,
+ isValidated = true,
+ )
+ )
+ // Trigger a data change in the same subscription group
+ connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
+ connectionsRepository.setMobileConnectivity(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = false,
+ )
+ )
+
+ assertThat(latest)
+ .isEqualTo(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = true,
+ )
+ )
+
+ job.cancel()
+ }
+
+ @Test
+ fun `data switch - in same group - validated matches previous value - expires after 2s`() =
+ testScope.runTest {
+ var latest: MobileConnectivityModel? = null
+ val job =
+ underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
+
+ connectionsRepository.setMobileConnectivity(
+ MobileConnectivityModel(
+ isConnected = true,
+ isValidated = true,
+ )
+ )
+ // Trigger a data change in the same subscription group
+ connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
+ connectionsRepository.setMobileConnectivity(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = false,
+ )
+ )
+ // After 1s, the force validation bit is still present
+ advanceTimeBy(1000)
+ assertThat(latest)
+ .isEqualTo(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = true,
+ )
+ )
+
+ // After 2s, the force validation expires
+ advanceTimeBy(1001)
+
+ assertThat(latest)
+ .isEqualTo(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = false,
+ )
+ )
+
+ job.cancel()
+ }
+
+ @Test
+ fun `data switch - in same group - not validated - uses new value immediately`() =
+ testScope.runTest {
+ var latest: MobileConnectivityModel? = null
+ val job =
+ underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
+
+ connectionsRepository.setMobileConnectivity(
+ MobileConnectivityModel(
+ isConnected = true,
+ isValidated = false,
+ )
+ )
+ connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
+ connectionsRepository.setMobileConnectivity(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = false,
+ )
+ )
+
+ assertThat(latest)
+ .isEqualTo(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = false,
+ )
+ )
+
+ job.cancel()
+ }
+
+ @Test
+ fun `data switch - lose validation - then switch happens - clears forced bit`() =
+ testScope.runTest {
+ var latest: MobileConnectivityModel? = null
+ val job =
+ underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
+
+ // GIVEN the network starts validated
+ connectionsRepository.setMobileConnectivity(
+ MobileConnectivityModel(
+ isConnected = true,
+ isValidated = true,
+ )
+ )
+
+ // WHEN a data change happens in the same group
+ connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
+
+ // WHEN the validation bit is lost
+ connectionsRepository.setMobileConnectivity(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = false,
+ )
+ )
+
+ // WHEN another data change happens in the same group
+ connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
+
+ // THEN the forced validation bit is still removed after 2s
+ assertThat(latest)
+ .isEqualTo(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = true,
+ )
+ )
+
+ advanceTimeBy(1000)
+
+ assertThat(latest)
+ .isEqualTo(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = true,
+ )
+ )
+
+ advanceTimeBy(1001)
+
+ assertThat(latest)
+ .isEqualTo(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = false,
+ )
+ )
+
+ job.cancel()
+ }
+
+ @Test
+ fun `data switch - while already forcing validation - resets clock`() =
+ testScope.runTest {
+ var latest: MobileConnectivityModel? = null
+ val job =
+ underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
+ connectionsRepository.setMobileConnectivity(
+ MobileConnectivityModel(
+ isConnected = true,
+ isValidated = true,
+ )
+ )
+
+ connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
+
+ advanceTimeBy(1000)
+
+ // WHEN another change in same group event happens
+ connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
+ connectionsRepository.setMobileConnectivity(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = false,
+ )
+ )
+
+ // THEN the forced validation remains for exactly 2 more seconds from now
+
+ // 1.500s from second event
+ advanceTimeBy(1500)
+ assertThat(latest)
+ .isEqualTo(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = true,
+ )
+ )
+
+ // 2.001s from the second event
+ advanceTimeBy(501)
+ assertThat(latest)
+ .isEqualTo(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = false,
+ )
+ )
+
+ job.cancel()
+ }
+
+ @Test
+ fun `data switch - not in same group - uses new values`() =
+ testScope.runTest {
+ var latest: MobileConnectivityModel? = null
+ val job =
+ underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
+
+ connectionsRepository.setMobileConnectivity(
+ MobileConnectivityModel(
+ isConnected = true,
+ isValidated = true,
+ )
+ )
+ connectionsRepository.setMobileConnectivity(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = false,
+ )
+ )
+
+ assertThat(latest)
+ .isEqualTo(
+ MobileConnectivityModel(
+ isConnected = false,
+ isValidated = false,
+ )
+ )
+
+ job.cancel()
+ }
+
companion object {
- private val IMMEDIATE = Dispatchers.Main.immediate
private val tableLogBuffer =
TableLogBuffer(8, "MobileIconsInteractorTest", FakeSystemClock())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 2a8d42ff6997..a24e29aebc1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -274,6 +274,41 @@ class MobileIconViewModelTest : SysuiTestCase() {
}
@Test
+ fun `network type - alwaysShow - shown when not connected`() =
+ testScope.runTest {
+ interactor.setIconGroup(THREE_G)
+ interactor.isConnected.value = false
+ interactor.alwaysShowDataRatIcon.value = true
+
+ var latest: Icon? = null
+ val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+ val expected =
+ Icon.Resource(
+ THREE_G.dataType,
+ ContentDescription.Resource(THREE_G.dataContentDescription)
+ )
+ assertThat(latest).isEqualTo(expected)
+
+ job.cancel()
+ }
+
+ @Test
+ fun `network type - not shown when not connected`() =
+ testScope.runTest {
+ interactor.setIconGroup(THREE_G)
+ interactor.isDataConnected.value = true
+ interactor.isConnected.value = false
+
+ var latest: Icon? = null
+ val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isNull()
+
+ job.cancel()
+ }
+
+ @Test
fun roaming() =
testScope.runTest {
interactor.isRoaming.value = true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt
index 30ac8d432e8a..824cebdc3c08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt
@@ -16,11 +16,12 @@
package com.android.systemui.statusbar.pipeline.wifi.data.model
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableRowLogger
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel.Active.Companion.MAX_VALID_LEVEL
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel.Active.Companion.MIN_VALID_LEVEL
+import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel.Companion.MIN_VALID_LEVEL
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -44,9 +45,53 @@ class WifiNetworkModelTest : SysuiTestCase() {
WifiNetworkModel.Active(NETWORK_ID, level = MAX_VALID_LEVEL + 1)
}
+ @Test(expected = IllegalArgumentException::class)
+ fun carrierMerged_invalidSubId_exceptionThrown() {
+ WifiNetworkModel.CarrierMerged(NETWORK_ID, INVALID_SUBSCRIPTION_ID, 1)
+ }
+
// Non-exhaustive logDiffs test -- just want to make sure the logging logic isn't totally broken
@Test
+ fun logDiffs_carrierMergedToInactive_resetsAllFields() {
+ val logger = TestLogger()
+ val prevVal =
+ WifiNetworkModel.CarrierMerged(
+ networkId = 5,
+ subscriptionId = 3,
+ level = 1,
+ )
+
+ WifiNetworkModel.Inactive.logDiffs(prevVal, logger)
+
+ assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_INACTIVE))
+ assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, NETWORK_ID_DEFAULT.toString()))
+ assertThat(logger.changes).contains(Pair(COL_VALIDATED, "false"))
+ assertThat(logger.changes).contains(Pair(COL_LEVEL, LEVEL_DEFAULT.toString()))
+ assertThat(logger.changes).contains(Pair(COL_SSID, "null"))
+ }
+
+ @Test
+ fun logDiffs_inactiveToCarrierMerged_logsAllFields() {
+ val logger = TestLogger()
+ val carrierMerged =
+ WifiNetworkModel.CarrierMerged(
+ networkId = 6,
+ subscriptionId = 3,
+ level = 2,
+ )
+
+ carrierMerged.logDiffs(prevVal = WifiNetworkModel.Inactive, logger)
+
+ assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED))
+ assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "6"))
+ assertThat(logger.changes).contains(Pair(COL_SUB_ID, "3"))
+ assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
+ assertThat(logger.changes).contains(Pair(COL_LEVEL, "2"))
+ assertThat(logger.changes).contains(Pair(COL_SSID, "null"))
+ }
+
+ @Test
fun logDiffs_inactiveToActive_logsAllActiveFields() {
val logger = TestLogger()
val activeNetwork =
@@ -95,8 +140,14 @@ class WifiNetworkModelTest : SysuiTestCase() {
level = 3,
ssid = "Test SSID"
)
+ val prevVal =
+ WifiNetworkModel.CarrierMerged(
+ networkId = 5,
+ subscriptionId = 3,
+ level = 1,
+ )
- activeNetwork.logDiffs(prevVal = WifiNetworkModel.CarrierMerged, logger)
+ activeNetwork.logDiffs(prevVal, logger)
assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_ACTIVE))
assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "5"))
@@ -105,7 +156,7 @@ class WifiNetworkModelTest : SysuiTestCase() {
assertThat(logger.changes).contains(Pair(COL_SSID, "Test SSID"))
}
@Test
- fun logDiffs_activeToCarrierMerged_resetsAllActiveFields() {
+ fun logDiffs_activeToCarrierMerged_logsAllFields() {
val logger = TestLogger()
val activeNetwork =
WifiNetworkModel.Active(
@@ -114,13 +165,20 @@ class WifiNetworkModelTest : SysuiTestCase() {
level = 3,
ssid = "Test SSID"
)
+ val carrierMerged =
+ WifiNetworkModel.CarrierMerged(
+ networkId = 6,
+ subscriptionId = 3,
+ level = 2,
+ )
- WifiNetworkModel.CarrierMerged.logDiffs(prevVal = activeNetwork, logger)
+ carrierMerged.logDiffs(prevVal = activeNetwork, logger)
assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED))
- assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, NETWORK_ID_DEFAULT.toString()))
- assertThat(logger.changes).contains(Pair(COL_VALIDATED, "false"))
- assertThat(logger.changes).contains(Pair(COL_LEVEL, LEVEL_DEFAULT.toString()))
+ assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "6"))
+ assertThat(logger.changes).contains(Pair(COL_SUB_ID, "3"))
+ assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
+ assertThat(logger.changes).contains(Pair(COL_LEVEL, "2"))
assertThat(logger.changes).contains(Pair(COL_SSID, "null"))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index 8f07615b19b2..87ce8faff5a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -26,6 +26,7 @@ import android.net.vcn.VcnTransportInfo
import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
import android.net.wifi.WifiManager.TrafficStateCallback
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -340,7 +341,6 @@ class WifiRepositoryImplTest : SysuiTestCase() {
.launchIn(this)
val wifiInfo = mock<WifiInfo>().apply {
- whenever(this.ssid).thenReturn(SSID)
whenever(this.isPrimary).thenReturn(true)
whenever(this.isCarrierMerged).thenReturn(true)
}
@@ -353,6 +353,67 @@ class WifiRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun wifiNetwork_carrierMergedButInvalidSubId_flowHasInvalid() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ val wifiInfo = mock<WifiInfo>().apply {
+ whenever(this.isPrimary).thenReturn(true)
+ whenever(this.isCarrierMerged).thenReturn(true)
+ whenever(this.subscriptionId).thenReturn(INVALID_SUBSCRIPTION_ID)
+ }
+
+ getNetworkCallback().onCapabilitiesChanged(
+ NETWORK,
+ createWifiNetworkCapabilities(wifiInfo),
+ )
+
+ assertThat(latest).isInstanceOf(WifiNetworkModel.Invalid::class.java)
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_isCarrierMerged_getsCorrectValues() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ val rssi = -57
+ val wifiInfo = mock<WifiInfo>().apply {
+ whenever(this.isPrimary).thenReturn(true)
+ whenever(this.isCarrierMerged).thenReturn(true)
+ whenever(this.rssi).thenReturn(rssi)
+ whenever(this.subscriptionId).thenReturn(567)
+ }
+
+ whenever(wifiManager.calculateSignalLevel(rssi)).thenReturn(2)
+ whenever(wifiManager.maxSignalLevel).thenReturn(5)
+
+ getNetworkCallback().onCapabilitiesChanged(
+ NETWORK,
+ createWifiNetworkCapabilities(wifiInfo),
+ )
+
+ assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
+ val latestCarrierMerged = latest as WifiNetworkModel.CarrierMerged
+ assertThat(latestCarrierMerged.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latestCarrierMerged.subscriptionId).isEqualTo(567)
+ assertThat(latestCarrierMerged.level).isEqualTo(2)
+ // numberOfLevels = maxSignalLevel + 1
+ assertThat(latestCarrierMerged.numberOfLevels).isEqualTo(6)
+
+ job.cancel()
+ }
+
+ @Test
fun wifiNetwork_notValidated_networkNotValidated() = runBlocking(IMMEDIATE) {
var latest: WifiNetworkModel? = null
val job = underTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index 01d59f96c221..089a170aa2be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -84,7 +84,9 @@ class WifiInteractorImplTest : SysuiTestCase() {
@Test
fun ssid_carrierMergedNetwork_outputsNull() = runBlocking(IMMEDIATE) {
- wifiRepository.setWifiNetwork(WifiNetworkModel.CarrierMerged)
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.CarrierMerged(networkId = 1, subscriptionId = 2, level = 1)
+ )
var latest: String? = "default"
val job = underTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index 726e813ec414..b9328377772a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -206,7 +206,8 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase
// Enabled = false => no networks shown
TestCase(
enabled = false,
- network = WifiNetworkModel.CarrierMerged,
+ network =
+ WifiNetworkModel.CarrierMerged(NETWORK_ID, subscriptionId = 1, level = 1),
expected = null,
),
TestCase(
@@ -228,7 +229,8 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase
// forceHidden = true => no networks shown
TestCase(
forceHidden = true,
- network = WifiNetworkModel.CarrierMerged,
+ network =
+ WifiNetworkModel.CarrierMerged(NETWORK_ID, subscriptionId = 1, level = 1),
expected = null,
),
TestCase(
@@ -369,7 +371,8 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase
// network = CarrierMerged => not shown
TestCase(
- network = WifiNetworkModel.CarrierMerged,
+ network =
+ WifiNetworkModel.CarrierMerged(NETWORK_ID, subscriptionId = 1, level = 1),
expected = null,
),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/FixedCapacityBatteryState.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/FixedCapacityBatteryState.kt
new file mode 100644
index 000000000000..7e010886c394
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/FixedCapacityBatteryState.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.stylus
+
+import android.hardware.BatteryState
+
+class FixedCapacityBatteryState(private val capacity: Float) : BatteryState() {
+ override fun getCapacity() = capacity
+ override fun getStatus() = 0
+ override fun isPresent() = true
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
index 117e00dd9b2a..1cccd65c8dbc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
@@ -85,6 +85,13 @@ class StylusUsiPowerStartableTest : SysuiTestCase() {
}
@Test
+ fun start_initStylusUsiPowerUi() {
+ startable.start()
+
+ verify(stylusUsiPowerUi, times(1)).init()
+ }
+
+ @Test
fun onStylusBluetoothConnected_refreshesNotification() {
startable.onStylusBluetoothConnected(STYLUS_DEVICE_ID, "ANY")
@@ -99,13 +106,21 @@ class StylusUsiPowerStartableTest : SysuiTestCase() {
}
@Test
- fun onStylusUsiBatteryStateChanged_batteryPresent_refreshesNotification() {
- val batteryState = mock(BatteryState::class.java)
- whenever(batteryState.isPresent).thenReturn(true)
+ fun onStylusUsiBatteryStateChanged_batteryPresentValidCapacity_refreshesNotification() {
+ val batteryState = FixedCapacityBatteryState(0.1f)
startable.onStylusUsiBatteryStateChanged(STYLUS_DEVICE_ID, 123, batteryState)
- verify(stylusUsiPowerUi, times(1)).updateBatteryState(batteryState)
+ verify(stylusUsiPowerUi, times(1)).updateBatteryState(STYLUS_DEVICE_ID, batteryState)
+ }
+
+ @Test
+ fun onStylusUsiBatteryStateChanged_batteryPresentInvalidCapacity_noop() {
+ val batteryState = FixedCapacityBatteryState(0f)
+
+ startable.onStylusUsiBatteryStateChanged(STYLUS_DEVICE_ID, 123, batteryState)
+
+ verifyNoMoreInteractions(stylusUsiPowerUi)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
index a7951f4fa068..1e81dc761b18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
@@ -17,8 +17,11 @@
package com.android.systemui.stylus
import android.app.Notification
-import android.hardware.BatteryState
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
import android.hardware.input.InputManager
+import android.os.Bundle
import android.os.Handler
import android.testing.AndroidTestingRunner
import android.view.InputDevice
@@ -27,8 +30,10 @@ import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Ignore
@@ -37,7 +42,10 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito.doNothing
import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
@@ -53,11 +61,16 @@ class StylusUsiPowerUiTest : SysuiTestCase() {
@Captor lateinit var notificationCaptor: ArgumentCaptor<Notification>
private lateinit var stylusUsiPowerUi: StylusUsiPowerUI
+ private lateinit var broadcastReceiver: BroadcastReceiver
+ private lateinit var contextSpy: Context
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ contextSpy = spy(mContext)
+ doNothing().whenever(contextSpy).startActivity(any())
+
whenever(handler.post(any())).thenAnswer {
(it.arguments[0] as Runnable).run()
true
@@ -68,12 +81,20 @@ class StylusUsiPowerUiTest : SysuiTestCase() {
whenever(btStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
// whenever(btStylusDevice.bluetoothAddress).thenReturn("SO:ME:AD:DR:ES")
- stylusUsiPowerUi = StylusUsiPowerUI(mContext, notificationManager, inputManager, handler)
+ stylusUsiPowerUi = StylusUsiPowerUI(contextSpy, notificationManager, inputManager, handler)
+ broadcastReceiver = stylusUsiPowerUi.receiver
+ }
+
+ @Test
+ fun updateBatteryState_capacityZero_noop() {
+ stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0f))
+
+ verifyNoMoreInteractions(notificationManager)
}
@Test
fun updateBatteryState_capacityBelowThreshold_notifies() {
- stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.1f))
+ stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f))
verify(notificationManager, times(1))
.notify(eq(R.string.stylus_battery_low_percentage), any())
@@ -82,7 +103,7 @@ class StylusUsiPowerUiTest : SysuiTestCase() {
@Test
fun updateBatteryState_capacityAboveThreshold_cancelsNotificattion() {
- stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.8f))
+ stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.8f))
verify(notificationManager, times(1)).cancel(R.string.stylus_battery_low_percentage)
verifyNoMoreInteractions(notificationManager)
@@ -90,8 +111,8 @@ class StylusUsiPowerUiTest : SysuiTestCase() {
@Test
fun updateBatteryState_existingNotification_capacityAboveThreshold_cancelsNotification() {
- stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.1f))
- stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.8f))
+ stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f))
+ stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.8f))
inOrder(notificationManager).let {
it.verify(notificationManager, times(1))
@@ -103,8 +124,8 @@ class StylusUsiPowerUiTest : SysuiTestCase() {
@Test
fun updateBatteryState_existingNotification_capacityBelowThreshold_updatesNotification() {
- stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.1f))
- stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.15f))
+ stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f))
+ stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.15f))
verify(notificationManager, times(2))
.notify(eq(R.string.stylus_battery_low_percentage), notificationCaptor.capture())
@@ -121,9 +142,9 @@ class StylusUsiPowerUiTest : SysuiTestCase() {
@Test
fun updateBatteryState_capacityAboveThenBelowThreshold_hidesThenShowsNotification() {
- stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.1f))
- stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.5f))
- stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.1f))
+ stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f))
+ stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.5f))
+ stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f))
inOrder(notificationManager).let {
it.verify(notificationManager, times(1))
@@ -145,7 +166,7 @@ class StylusUsiPowerUiTest : SysuiTestCase() {
@Test
fun updateSuppression_existingNotification_cancelsNotification() {
- stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.1f))
+ stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f))
stylusUsiPowerUi.updateSuppression(true)
@@ -159,18 +180,18 @@ class StylusUsiPowerUiTest : SysuiTestCase() {
@Test
@Ignore("TODO(b/257936830): get bt address once input api available")
- fun refresh_hasConnectedBluetoothStylus_doesNotNotify() {
+ fun refresh_hasConnectedBluetoothStylus_cancelsNotification() {
whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(0))
stylusUsiPowerUi.refresh()
- verifyNoMoreInteractions(notificationManager)
+ verify(notificationManager).cancel(R.string.stylus_battery_low_percentage)
}
@Test
@Ignore("TODO(b/257936830): get bt address once input api available")
fun refresh_hasConnectedBluetoothStylus_existingNotification_cancelsNotification() {
- stylusUsiPowerUi.updateBatteryState(FixedCapacityBatteryState(0.1f))
+ stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f))
whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(0))
stylusUsiPowerUi.refresh()
@@ -178,9 +199,27 @@ class StylusUsiPowerUiTest : SysuiTestCase() {
verify(notificationManager).cancel(R.string.stylus_battery_low_percentage)
}
- class FixedCapacityBatteryState(private val capacity: Float) : BatteryState() {
- override fun getCapacity() = capacity
- override fun getStatus() = 0
- override fun isPresent() = true
+ @Test
+ fun broadcastReceiver_clicked_hasInputDeviceId_startsUsiDetailsActivity() {
+ val intent = Intent(StylusUsiPowerUI.ACTION_CLICKED_LOW_BATTERY)
+ val activityIntentCaptor = argumentCaptor<Intent>()
+ stylusUsiPowerUi.updateBatteryState(1, FixedCapacityBatteryState(0.15f))
+ broadcastReceiver.onReceive(contextSpy, intent)
+
+ verify(contextSpy, times(1)).startActivity(activityIntentCaptor.capture())
+ assertThat(activityIntentCaptor.value.action)
+ .isEqualTo(StylusUsiPowerUI.ACTION_STYLUS_USI_DETAILS)
+ val args =
+ activityIntentCaptor.value.getExtra(StylusUsiPowerUI.KEY_SETTINGS_FRAGMENT_ARGS)
+ as Bundle
+ assertThat(args.getInt(StylusUsiPowerUI.KEY_DEVICE_INPUT_ID)).isEqualTo(1)
+ }
+
+ @Test
+ fun broadcastReceiver_clicked_nullInputDeviceId_doesNotStartActivity() {
+ val intent = Intent(StylusUsiPowerUI.ACTION_CLICKED_LOW_BATTERY)
+ broadcastReceiver.onReceive(contextSpy, intent)
+
+ verify(contextSpy, never()).startActivity(any())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index 30c4f97e5503..f4226bcd71c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -175,8 +175,10 @@ class FoldAodAnimationControllerTest : SysuiTestCase() {
fold()
underTest.onScreenTurningOn({})
- underTest.onStartedWakingUp()
+ // The body of onScreenTurningOn is executed on fakeExecutor,
+ // run all pending tasks before calling the next method
fakeExecutor.runAllReady()
+ underTest.onStartedWakingUp()
verify(latencyTracker).onActionStart(any())
verify(latencyTracker).onActionCancel(any())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt
index c31640279305..4a28cd1de255 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt
@@ -26,6 +26,10 @@ class TestUnfoldTransitionProvider : UnfoldTransitionProgressProvider, Transitio
listeners.forEach { it.onTransitionFinished() }
}
+ override fun onTransitionFinishing() {
+ listeners.forEach { it.onTransitionFinishing() }
+ }
+
override fun onTransitionProgress(progress: Float) {
listeners.forEach { it.onTransitionProgress(progress) }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
new file mode 100644
index 000000000000..d3fdbd94a5ac
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.unfold
+
+import android.os.VibrationEffect
+import android.os.Vibrator
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.updates.FoldProvider
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import java.util.concurrent.Executor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class UnfoldHapticsPlayerTest : SysuiTestCase() {
+
+ private val progressProvider = TestUnfoldTransitionProvider()
+ private val vibrator: Vibrator = mock()
+ private val testFoldProvider = TestFoldProvider()
+
+ private lateinit var player: UnfoldHapticsPlayer
+
+ @Before
+ fun before() {
+ player = UnfoldHapticsPlayer(progressProvider, testFoldProvider, Runnable::run, vibrator)
+ }
+
+ @Test
+ fun testUnfoldingTransitionFinishingEarly_playsHaptics() {
+ testFoldProvider.onFoldUpdate(isFolded = true)
+ testFoldProvider.onFoldUpdate(isFolded = false)
+ progressProvider.onTransitionStarted()
+ progressProvider.onTransitionProgress(0.5f)
+ progressProvider.onTransitionFinishing()
+
+ verify(vibrator).vibrate(any<VibrationEffect>())
+ }
+
+ @Test
+ fun testUnfoldingTransitionFinishingLate_doesNotPlayHaptics() {
+ testFoldProvider.onFoldUpdate(isFolded = true)
+ testFoldProvider.onFoldUpdate(isFolded = false)
+ progressProvider.onTransitionStarted()
+ progressProvider.onTransitionProgress(0.99f)
+ progressProvider.onTransitionFinishing()
+
+ verify(vibrator, never()).vibrate(any<VibrationEffect>())
+ }
+
+ @Test
+ fun testFoldingAfterUnfolding_doesNotPlayHaptics() {
+ // Unfold
+ testFoldProvider.onFoldUpdate(isFolded = true)
+ testFoldProvider.onFoldUpdate(isFolded = false)
+ progressProvider.onTransitionStarted()
+ progressProvider.onTransitionProgress(0.5f)
+ progressProvider.onTransitionFinishing()
+ progressProvider.onTransitionFinished()
+ clearInvocations(vibrator)
+
+ // Fold
+ progressProvider.onTransitionStarted()
+ progressProvider.onTransitionProgress(0.5f)
+ progressProvider.onTransitionFinished()
+ testFoldProvider.onFoldUpdate(isFolded = true)
+
+ verify(vibrator, never()).vibrate(any<VibrationEffect>())
+ }
+
+ @Test
+ fun testUnfoldingAfterFoldingAndUnfolding_playsHaptics() {
+ // Unfold
+ testFoldProvider.onFoldUpdate(isFolded = true)
+ testFoldProvider.onFoldUpdate(isFolded = false)
+ progressProvider.onTransitionStarted()
+ progressProvider.onTransitionProgress(0.5f)
+ progressProvider.onTransitionFinishing()
+ progressProvider.onTransitionFinished()
+
+ // Fold
+ progressProvider.onTransitionStarted()
+ progressProvider.onTransitionProgress(0.5f)
+ progressProvider.onTransitionFinished()
+ testFoldProvider.onFoldUpdate(isFolded = true)
+ clearInvocations(vibrator)
+
+ // Unfold again
+ testFoldProvider.onFoldUpdate(isFolded = true)
+ testFoldProvider.onFoldUpdate(isFolded = false)
+ progressProvider.onTransitionStarted()
+ progressProvider.onTransitionProgress(0.5f)
+ progressProvider.onTransitionFinishing()
+ progressProvider.onTransitionFinished()
+
+ verify(vibrator).vibrate(any<VibrationEffect>())
+ }
+
+ private class TestFoldProvider : FoldProvider {
+ private val listeners = arrayListOf<FoldProvider.FoldCallback>()
+
+ override fun registerCallback(callback: FoldProvider.FoldCallback, executor: Executor) {
+ listeners += callback
+ }
+
+ override fun unregisterCallback(callback: FoldProvider.FoldCallback) {
+ listeners -= callback
+ }
+
+ fun onFoldUpdate(isFolded: Boolean) {
+ listeners.forEach { it.onFoldUpdated(isFolded) }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index bf2235aa98a9..1bab99787de4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -132,8 +132,10 @@ public abstract class SysuiTestCase {
@After
public void SysuiTeardown() {
- InstrumentationRegistry.registerInstance(mRealInstrumentation,
- InstrumentationRegistry.getArguments());
+ if (mRealInstrumentation != null) {
+ InstrumentationRegistry.registerInstance(mRealInstrumentation,
+ InstrumentationRegistry.getArguments());
+ }
if (TestableLooper.get(this) != null) {
TestableLooper.get(this).processAllMessages();
// Must remove static reference to this test object to prevent leak (b/261039202)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt
index 8176dd07b84a..1bdee3667d04 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt
@@ -19,9 +19,10 @@ package com.android.systemui.biometrics.udfps
import android.graphics.Rect
class FakeOverlapDetector : OverlapDetector {
- var shouldReturn: Boolean = false
+ var shouldReturn: Map<Int, Boolean> = mapOf()
override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean {
- return shouldReturn
+ return shouldReturn[touchData.pointerId]
+ ?: error("Unexpected PointerId not declared in TestCase currentPointers")
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index 2d6d29a50a74..926c6c56a862 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -98,6 +98,10 @@ public class FakeStatusBarIconController extends BaseLeakChecker<IconManager>
}
@Override
+ public void removeAllIconsForExternalSlot(String slot) {
+ }
+
+ @Override
public void setIconAccessibilityLiveRegion(String slot, int mode) {
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
index 5a868a4df354..cfb959e51d4e 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -22,8 +22,8 @@ import android.hardware.SensorManager
import android.os.Handler
import android.view.IWindowManager
import com.android.systemui.unfold.config.UnfoldTransitionConfig
-import com.android.systemui.unfold.dagger.UnfoldBackground
import com.android.systemui.unfold.dagger.UnfoldMain
+import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
@@ -58,7 +58,7 @@ interface UnfoldSharedComponent {
@BindsInstance sensorManager: SensorManager,
@BindsInstance @UnfoldMain handler: Handler,
@BindsInstance @UnfoldMain executor: Executor,
- @BindsInstance @UnfoldBackground backgroundExecutor: Executor,
+ @BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor,
@BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
@BindsInstance windowManager: IWindowManager,
@BindsInstance contentResolver: ContentResolver = context.contentResolver
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
index 3fa546914d3a..31616fa54bf4 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -16,9 +16,7 @@
package com.android.systemui.unfold
-import android.hardware.SensorManager
import com.android.systemui.unfold.config.UnfoldTransitionConfig
-import com.android.systemui.unfold.dagger.UnfoldBackground
import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
@@ -34,55 +32,18 @@ import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider
import dagger.Module
import dagger.Provides
import java.util.Optional
-import java.util.concurrent.Executor
+import javax.inject.Provider
import javax.inject.Singleton
-@Module
+@Module(includes = [UnfoldSharedInternalModule::class])
class UnfoldSharedModule {
@Provides
@Singleton
- fun unfoldTransitionProgressProvider(
- config: UnfoldTransitionConfig,
- scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
- tracingListener: ATraceLoggerTransitionProgressListener,
- foldStateProvider: FoldStateProvider
- ): Optional<UnfoldTransitionProgressProvider> =
- if (!config.isEnabled) {
- Optional.empty()
- } else {
- val baseProgressProvider =
- if (config.isHingeAngleEnabled) {
- PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
- } else {
- FixedTimingTransitionProgressProvider(foldStateProvider)
- }
- Optional.of(
- scaleAwareProviderFactory.wrap(baseProgressProvider).apply {
- // Always present callback that logs animation beginning and end.
- addCallback(tracingListener)
- }
- )
- }
-
- @Provides
- @Singleton
fun provideFoldStateProvider(
deviceFoldStateProvider: DeviceFoldStateProvider
): FoldStateProvider = deviceFoldStateProvider
@Provides
- fun hingeAngleProvider(
- config: UnfoldTransitionConfig,
- sensorManager: SensorManager,
- @UnfoldBackground executor: Executor
- ): HingeAngleProvider =
- if (config.isHingeAngleEnabled) {
- HingeSensorAngleProvider(sensorManager, executor)
- } else {
- EmptyHingeAngleProvider
- }
-
- @Provides
@Singleton
fun unfoldKeyguardVisibilityProvider(
impl: UnfoldKeyguardVisibilityManagerImpl
@@ -94,3 +55,51 @@ class UnfoldSharedModule {
impl: UnfoldKeyguardVisibilityManagerImpl
): UnfoldKeyguardVisibilityManager = impl
}
+
+/**
+ * Needed as methods inside must be public, but their parameters can be internal (and, a public
+ * method can't have internal parameters). Making the module internal and included in a public one
+ * fixes the issue.
+ */
+@Module
+internal class UnfoldSharedInternalModule {
+ @Provides
+ @Singleton
+ fun unfoldTransitionProgressProvider(
+ config: UnfoldTransitionConfig,
+ scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
+ tracingListener: ATraceLoggerTransitionProgressListener,
+ physicsBasedUnfoldTransitionProgressProvider:
+ Provider<PhysicsBasedUnfoldTransitionProgressProvider>,
+ fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>,
+ ): Optional<UnfoldTransitionProgressProvider> {
+ if (!config.isEnabled) {
+ return Optional.empty()
+ }
+ val baseProgressProvider =
+ if (config.isHingeAngleEnabled) {
+ physicsBasedUnfoldTransitionProgressProvider.get()
+ } else {
+ fixedTimingTransitionProgressProvider.get()
+ }
+
+ return Optional.of(
+ scaleAwareProviderFactory.wrap(baseProgressProvider).apply {
+ // Always present callback that logs animation beginning and end.
+ addCallback(tracingListener)
+ }
+ )
+ }
+
+ @Provides
+ fun hingeAngleProvider(
+ config: UnfoldTransitionConfig,
+ hingeAngleSensorProvider: Provider<HingeSensorAngleProvider>
+ ): HingeAngleProvider {
+ return if (config.isHingeAngleEnabled) {
+ hingeAngleSensorProvider.get()
+ } else {
+ EmptyHingeAngleProvider
+ }
+ }
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index a1ed17844e8e..aa93c6290145 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -37,29 +37,29 @@ import java.util.concurrent.Executor
* This should **never** be called from sysui, as the object is already provided in that process.
*/
fun createUnfoldSharedComponent(
- context: Context,
- config: UnfoldTransitionConfig,
- screenStatusProvider: ScreenStatusProvider,
- foldProvider: FoldProvider,
- activityTypeProvider: CurrentActivityTypeProvider,
- sensorManager: SensorManager,
- mainHandler: Handler,
- mainExecutor: Executor,
- backgroundExecutor: Executor,
- tracingTagPrefix: String,
- windowManager: IWindowManager,
+ context: Context,
+ config: UnfoldTransitionConfig,
+ screenStatusProvider: ScreenStatusProvider,
+ foldProvider: FoldProvider,
+ activityTypeProvider: CurrentActivityTypeProvider,
+ sensorManager: SensorManager,
+ mainHandler: Handler,
+ mainExecutor: Executor,
+ singleThreadBgExecutor: Executor,
+ tracingTagPrefix: String,
+ windowManager: IWindowManager,
): UnfoldSharedComponent =
- DaggerUnfoldSharedComponent.factory()
- .create(
- context,
- config,
- screenStatusProvider,
- foldProvider,
- activityTypeProvider,
- sensorManager,
- mainHandler,
- mainExecutor,
- backgroundExecutor,
- tracingTagPrefix,
- windowManager,
- )
+ DaggerUnfoldSharedComponent.factory()
+ .create(
+ context,
+ config,
+ screenStatusProvider,
+ foldProvider,
+ activityTypeProvider,
+ sensorManager,
+ mainHandler,
+ mainExecutor,
+ singleThreadBgExecutor,
+ tracingTagPrefix,
+ windowManager,
+ )
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldSingleThreadBg.kt
index 60747954dac3..dcac531485e3 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldSingleThreadBg.kt
@@ -18,8 +18,7 @@ import javax.inject.Qualifier
/**
* Alternative to [UiBackground] qualifier annotation in unfold module.
+ *
* It is needed as we can't depend on SystemUI code in this module.
*/
-@Qualifier
-@Retention(AnnotationRetention.RUNTIME)
-annotation class UnfoldBackground
+@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class UnfoldSingleThreadBg
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
index fa59cb4d12be..4622464b204d 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
@@ -24,11 +24,13 @@ import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
+import javax.inject.Inject
/** Emits animation progress with fixed timing after unfolding */
-internal class FixedTimingTransitionProgressProvider(
- private val foldStateProvider: FoldStateProvider
-) : UnfoldTransitionProgressProvider, FoldStateProvider.FoldUpdatesListener {
+internal class FixedTimingTransitionProgressProvider
+@Inject
+constructor(private val foldStateProvider: FoldStateProvider) :
+ UnfoldTransitionProgressProvider, FoldStateProvider.FoldUpdatesListener {
private val animatorListener = AnimatorListener()
private val animator =
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index 074b1e162fed..6ffbe5aa25c0 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -33,9 +33,10 @@ import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
import com.android.systemui.unfold.updates.name
+import javax.inject.Inject
/** Maps fold updates to unfold transition progress using DynamicAnimation. */
-class PhysicsBasedUnfoldTransitionProgressProvider(
+class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor(
private val foldStateProvider: FoldStateProvider
) : UnfoldTransitionProgressProvider, FoldUpdatesListener, DynamicAnimation.OnAnimationEndListener {
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 5b458975fa34..97c9ba99f096 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -79,6 +79,7 @@ constructor(
screenStatusProvider.addCallback(screenListener)
hingeAngleProvider.addCallback(hingeAngleListener)
rotationChangeProvider.addCallback(rotationListener)
+ activityTypeProvider.init()
}
override fun stop() {
@@ -87,6 +88,7 @@ constructor(
hingeAngleProvider.removeCallback(hingeAngleListener)
hingeAngleProvider.stop()
rotationChangeProvider.removeCallback(rotationListener)
+ activityTypeProvider.uninit()
}
override fun addCallback(listener: FoldUpdatesListener) {
@@ -115,19 +117,17 @@ constructor(
}
val isClosing = angle < lastHingeAngle
- val closingThreshold = getClosingThreshold()
- val closingThresholdMet = closingThreshold == null || angle < closingThreshold
val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES
val closingEventDispatched = lastFoldUpdate == FOLD_UPDATE_START_CLOSING
val screenAvailableEventSent = isUnfoldHandled
if (isClosing // hinge angle should be decreasing since last update
- && closingThresholdMet // hinge angle is below certain threshold
&& !closingEventDispatched // we haven't sent closing event already
&& !isFullyOpened // do not send closing event if we are in fully opened hinge
// angle range as closing threshold could overlap this range
&& screenAvailableEventSent // do not send closing event if we are still in
// the process of turning on the inner display
+ && isClosingThresholdMet(angle) // hinge angle is below certain threshold.
) {
notifyFoldUpdate(FOLD_UPDATE_START_CLOSING)
}
@@ -146,6 +146,11 @@ constructor(
outputListeners.forEach { it.onHingeAngleUpdate(angle) }
}
+ private fun isClosingThresholdMet(currentAngle: Float) : Boolean {
+ val closingThreshold = getClosingThreshold()
+ return closingThreshold == null || currentAngle < closingThreshold
+ }
+
/**
* Fold animation should be started only after the threshold returned here.
*
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
index 577137ca12f3..89fb12e313ec 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
@@ -20,35 +20,43 @@ import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Trace
import androidx.core.util.Consumer
+import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
import java.util.concurrent.Executor
+import javax.inject.Inject
-internal class HingeSensorAngleProvider(
+internal class HingeSensorAngleProvider
+@Inject
+constructor(
private val sensorManager: SensorManager,
- private val executor: Executor
-) :
- HingeAngleProvider {
+ @UnfoldSingleThreadBg private val singleThreadBgExecutor: Executor
+) : HingeAngleProvider {
private val sensorListener = HingeAngleSensorListener()
private val listeners: MutableList<Consumer<Float>> = arrayListOf()
var started = false
- override fun start() = executor.execute {
- if (started) return@execute
- Trace.beginSection("HingeSensorAngleProvider#start")
- val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE)
- sensorManager.registerListener(
- sensorListener,
- sensor,
- SensorManager.SENSOR_DELAY_FASTEST
- )
- Trace.endSection()
- started = true
+ override fun start() {
+ singleThreadBgExecutor.execute {
+ if (started) return@execute
+ Trace.beginSection("HingeSensorAngleProvider#start")
+ val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE)
+ sensorManager.registerListener(
+ sensorListener,
+ sensor,
+ SensorManager.SENSOR_DELAY_FASTEST
+ )
+ Trace.endSection()
+
+ started = true
+ }
}
- override fun stop() = executor.execute {
- if (!started) return@execute
- sensorManager.unregisterListener(sensorListener)
- started = false
+ override fun stop() {
+ singleThreadBgExecutor.execute {
+ if (!started) return@execute
+ sensorManager.unregisterListener(sensorListener)
+ started = false
+ }
}
override fun removeCallback(listener: Consumer<Float>) {
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt
index d0e6cdc9a3c6..34e7c38c1e59 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt
@@ -16,6 +16,11 @@ package com.android.systemui.unfold.util
interface CurrentActivityTypeProvider {
val isHomeActivity: Boolean?
+
+ /** Starts listening for task updates. */
+ fun init() {}
+ /** Stop listening for task updates. */
+ fun uninit() {}
}
class EmptyCurrentActivityTypeProvider(override val isHomeActivity: Boolean? = null) :
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 4f1efd627e40..425158195940 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -3091,7 +3091,7 @@ public class AccountManagerService
}
}
- Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
+ Intent intent = result.getParcelable(AccountManager.KEY_INTENT, Intent.class);
if (intent != null && notifyOnAuthFailure && !customTokens) {
/*
* Make sure that the supplied intent is owned by the authenticator
@@ -3516,8 +3516,7 @@ public class AccountManagerService
Bundle.setDefusable(result, true);
mNumResults++;
Intent intent = null;
- if (result != null
- && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
+ if (result != null) {
if (!checkKeyIntent(
Binder.getCallingUid(),
result)) {
@@ -4885,8 +4884,10 @@ public class AccountManagerService
EventLog.writeEvent(0x534e4554, "250588548", authUid, "");
return false;
}
-
Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT, Intent.class);
+ if (intent == null) {
+ return true;
+ }
// Explicitly set an empty ClipData to ensure that we don't offer to
// promote any Uris contained inside for granting purposes
if (intent.getClipData() == null) {
@@ -4936,8 +4937,12 @@ public class AccountManagerService
Bundle simulateBundle = p.readBundle();
p.recycle();
Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT, Intent.class);
- return (intent.filterEquals(simulateBundle.getParcelable(AccountManager.KEY_INTENT,
- Intent.class)));
+ Intent simulateIntent = simulateBundle.getParcelable(AccountManager.KEY_INTENT,
+ Intent.class);
+ if (intent == null) {
+ return (simulateIntent == null);
+ }
+ return intent.filterEquals(simulateIntent);
}
private boolean isExportedSystemActivity(ActivityInfo activityInfo) {
@@ -5082,8 +5087,7 @@ public class AccountManagerService
}
}
}
- if (result != null
- && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
+ if (result != null) {
if (!checkKeyIntent(
Binder.getCallingUid(),
result)) {
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 17cd5d158f2d..6562ae70bc19 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -97,7 +97,6 @@ public class SettingsToPropertiesMapper {
DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
DeviceConfig.NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT,
DeviceConfig.NAMESPACE_SWCODEC_NATIVE,
- DeviceConfig.NAMESPACE_TETHERING,
DeviceConfig.NAMESPACE_VENDOR_SYSTEM_NATIVE,
DeviceConfig.NAMESPACE_VENDOR_SYSTEM_NATIVE_BOOT,
DeviceConfig.NAMESPACE_VIRTUALIZATION_FRAMEWORK_NATIVE,
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index cdcf6c4ea206..662db787ebe2 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -108,7 +108,7 @@ public class PreferencesHelper implements RankingConfig {
@VisibleForTesting
static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000;
@VisibleForTesting
- static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 50000;
+ static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 6000;
private static final int NOTIFICATION_PREFERENCES_PULL_LIMIT = 1000;
private static final int NOTIFICATION_CHANNEL_PULL_LIMIT = 2000;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 554e2690b878..20c9a211e586 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -4215,7 +4215,6 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
boolean changed = false;
- Set<Permission> needsUpdate = null;
synchronized (mLock) {
final Iterator<Permission> it = mRegistry.getPermissionTrees().iterator();
while (it.hasNext()) {
@@ -4234,26 +4233,6 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
+ " that used to be declared by " + bp.getPackageName());
it.remove();
}
- if (needsUpdate == null) {
- needsUpdate = new ArraySet<>();
- }
- needsUpdate.add(bp);
- }
- }
- if (needsUpdate != null) {
- for (final Permission bp : needsUpdate) {
- final AndroidPackage sourcePkg =
- mPackageManagerInt.getPackage(bp.getPackageName());
- final PackageStateInternal sourcePs =
- mPackageManagerInt.getPackageStateInternal(bp.getPackageName());
- synchronized (mLock) {
- if (sourcePkg != null && sourcePs != null) {
- continue;
- }
- Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
- + " from package " + bp.getPackageName());
- mRegistry.removePermission(bp.getName());
- }
}
}
return changed;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 5285f63dcc44..b55b6dd0eb55 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4129,9 +4129,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyEvent.KEYCODE_DEMO_APP_2:
case KeyEvent.KEYCODE_DEMO_APP_3:
case KeyEvent.KEYCODE_DEMO_APP_4: {
- // TODO(b/254604589): Dispatch KeyEvent to System UI.
- sendSystemKeyToStatusBarAsync(keyCode);
-
// Just drop if keys are not intercepted for direct key.
result &= ~ACTION_PASS_TO_USER;
break;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index e7221c850da8..fde0b34c7836 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -191,4 +191,12 @@ public interface StatusBarManagerInternal {
* @see com.android.internal.statusbar.IStatusBar#enterStageSplitFromRunningApp
*/
void enterStageSplitFromRunningApp(boolean leftOrTop);
+
+ /**
+ * Shows the media output switcher dialog.
+ *
+ * @param packageName of the session for which the output switcher is shown.
+ * @see com.android.internal.statusbar.IStatusBar#showMediaOutputSwitcher
+ */
+ void showMediaOutputSwitcher(String packageName);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 45748e6ce76d..ab7292d49c7d 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -714,6 +714,16 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
} catch (RemoteException ex) { }
}
}
+
+ @Override
+ public void showMediaOutputSwitcher(String packageName) {
+ if (mBar != null) {
+ try {
+ mBar.showMediaOutputSwitcher(packageName);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
};
private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 1ba7e687a553..861961599ada 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4715,6 +4715,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mTaskChangeNotificationController.notifyTaskFocusChanged(prevTask.mTaskId, false);
}
mTaskChangeNotificationController.notifyTaskFocusChanged(task.mTaskId, true);
+ mTaskSupervisor.mRecentTasks.add(task);
}
applyUpdateLockStateLocked(r);
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 75ba2146267d..9c43c1d62ab8 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -1146,10 +1146,9 @@ final class LetterboxUiController {
final ActivityRecord firstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity(
ActivityRecord::fillsParent, mActivityRecord, false /* includeBoundary */,
true /* traverseTopToBottom */);
- if (firstOpaqueActivityBeneath == null
- || mActivityRecord.launchedFromUid != firstOpaqueActivityBeneath.getUid()) {
+ if (firstOpaqueActivityBeneath == null) {
// We skip letterboxing if the translucent activity doesn't have any opaque
- // activities beneath of if it's launched from a different user (e.g. notification)
+ // activities beneath
return;
}
inheritConfiguration(firstOpaqueActivityBeneath);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a464112f0492..beaac05d16a6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -609,7 +609,7 @@ class Task extends TaskFragment {
*/
ActivityRecord mChildPipActivity;
- boolean mLastSurfaceShowing = true;
+ boolean mLastSurfaceShowing;
/**
* Tracks if a back gesture is in progress.
@@ -4197,13 +4197,7 @@ class Task extends TaskFragment {
@Override
boolean showSurfaceOnCreation() {
- if (mCreatedByOrganizer) {
- // Tasks created by the organizer are default visible because they can synchronously
- // update the leash before new children are added to the task.
- return true;
- }
- // Organized tasks handle their own surface visibility
- return !canBeOrganized();
+ return false;
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 8ad76a3eb327..79be9463aa05 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1074,10 +1074,14 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// Use launch-adjacent-flag-root if launching with launch-adjacent flag.
if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
&& mLaunchAdjacentFlagRootTask != null) {
- // If the adjacent launch is coming from the same root, launch to adjacent root instead.
- if (sourceTask != null && mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment() != null
+ if (sourceTask != null && sourceTask == candidateTask) {
+ // Do nothing when task that is getting opened is same as the source.
+ } else if (sourceTask != null
+ && mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment() != null
&& (sourceTask == mLaunchAdjacentFlagRootTask
|| sourceTask.isDescendantOf(mLaunchAdjacentFlagRootTask))) {
+ // If the adjacent launch is coming from the same root, launch to
+ // adjacent root instead.
return mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment().asTask();
} else {
return mLaunchAdjacentFlagRootTask;
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 90a0dffa25f2..49b2a4ef51a7 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -540,9 +540,12 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
synchronized (mGlobalLock) {
final TaskFragmentOrganizerState organizerState =
mTaskFragmentOrganizerState.get(organizer.asBinder());
- return organizerState != null
- ? organizerState.mRemoteAnimationDefinition
- : null;
+ if (organizerState == null) {
+ Slog.e(TAG, "TaskFragmentOrganizer has been unregistered or died when trying"
+ + " to play animation on its organized windows.");
+ return null;
+ }
+ return organizerState.mRemoteAnimationDefinition;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 38613a655d70..fd477532e984 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1962,6 +1962,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
ownerTask.addChild(taskFragment, position);
taskFragment.setWindowingMode(creationParams.getWindowingMode());
taskFragment.setBounds(creationParams.getInitialBounds());
+ // Record the initial relative embedded bounds.
+ taskFragment.updateRelativeEmbeddedBounds();
mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
if (transition != null) transition.collectExistenceChange(taskFragment);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 63607ad19f5a..ae03fbbe1cff 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -396,14 +396,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
int mPrepareSyncSeqId = 0;
/**
- * {@code true} when the client was still drawing for sync when the sync-set was finished or
- * cancelled. This can happen if the window goes away during a sync. In this situation we need
- * to make sure to still apply the postDrawTransaction when it finishes to prevent the client
- * from getting stuck in a bad state.
- */
- boolean mClientWasDrawingForSync = false;
-
- /**
* Special mode that is intended only for the rounded corner overlay: during rotation
* transition, we un-rotate the window token such that the window appears as it did before the
* rotation.
@@ -6024,9 +6016,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
void finishSync(Transaction outMergedTransaction, boolean cancel) {
- if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mRedrawForSyncReported) {
- mClientWasDrawingForSync = true;
- }
mPrepareSyncSeqId = 0;
if (cancel) {
// This is leaving sync so any buffers left in the sync have a chance of
@@ -6094,9 +6083,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
layoutNeeded = onSyncFinishedDrawing();
}
- layoutNeeded |=
- mWinAnimator.finishDrawingLocked(postDrawTransaction, mClientWasDrawingForSync);
- mClientWasDrawingForSync = false;
+ layoutNeeded |= mWinAnimator.finishDrawingLocked(postDrawTransaction);
// We always want to force a traversal after a finish draw for blast sync.
return !skipLayout && (hasSyncHandlers || layoutNeeded);
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index a0ba8fda906f..f3642480da58 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -151,17 +151,6 @@ class WindowStateAnimator {
int mAttrType;
- /**
- * Handles surface changes synchronized to after the client has drawn the surface. This
- * transaction is currently used to reparent the old surface children to the new surface once
- * the client has completed drawing to the new surface.
- * This transaction is also used to merge transactions parceled in by the client. The client
- * uses the transaction to update the relative z of its children from the old parent surface
- * to the new parent surface once window manager reparents its children.
- */
- private final SurfaceControl.Transaction mPostDrawTransaction =
- new SurfaceControl.Transaction();
-
WindowStateAnimator(final WindowState win) {
final WindowManagerService service = win.mWmService;
@@ -217,8 +206,7 @@ class WindowStateAnimator {
}
}
- boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction,
- boolean forceApplyNow) {
+ boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction) {
final boolean startingWindow =
mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
if (startingWindow) {
@@ -240,14 +228,7 @@ class WindowStateAnimator {
}
if (postDrawTransaction != null) {
- // If there is no surface, the last draw was for the previous surface. We don't want to
- // wait until the new surface is shown and instead just apply the transaction right
- // away.
- if (mLastHidden && mDrawState != NO_SURFACE && !forceApplyNow) {
- mPostDrawTransaction.merge(postDrawTransaction);
- } else {
- mWin.getSyncTransaction().merge(postDrawTransaction);
- }
+ mWin.getSyncTransaction().merge(postDrawTransaction);
layoutNeeded = true;
}
@@ -547,7 +528,6 @@ class WindowStateAnimator {
if (!shown)
return false;
- t.merge(mPostDrawTransaction);
return true;
}
@@ -714,10 +694,6 @@ class WindowStateAnimator {
}
void destroySurface(SurfaceControl.Transaction t) {
- // Since the SurfaceControl is getting torn down, it's safe to just clean up any
- // pending transactions that were in mPostDrawTransaction, as well.
- t.merge(mPostDrawTransaction);
-
try {
if (mSurfaceController != null) {
mSurfaceController.destroy(t);
diff --git a/services/people/java/com/android/server/people/data/ContactsQueryHelper.java b/services/people/java/com/android/server/people/data/ContactsQueryHelper.java
index 8a3a44ae9f35..0993295e162f 100644
--- a/services/people/java/com/android/server/people/data/ContactsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/ContactsQueryHelper.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.content.Context;
import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
@@ -149,6 +150,8 @@ class ContactsQueryHelper {
found = true;
}
+ } catch (SQLiteException exception) {
+ Slog.w("SQLite exception when querying contacts.", exception);
}
if (found && lookupKey != null && hasPhoneNumber) {
return queryPhoneNumber(lookupKey);
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 693f3a0cf8a0..bd0a814e172a 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -271,22 +271,22 @@ public class DataManager {
private ConversationChannel getConversationChannel(String packageName, int userId,
String shortcutId, ConversationInfo conversationInfo) {
ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId);
- return getConversationChannel(shortcutInfo, conversationInfo);
+ return getConversationChannel(
+ shortcutInfo, conversationInfo, packageName, userId, shortcutId);
}
@Nullable
private ConversationChannel getConversationChannel(ShortcutInfo shortcutInfo,
- ConversationInfo conversationInfo) {
+ ConversationInfo conversationInfo, String packageName, int userId, String shortcutId) {
if (conversationInfo == null || conversationInfo.isDemoted()) {
return null;
}
if (shortcutInfo == null) {
- Slog.e(TAG, " Shortcut no longer found");
+ Slog.e(TAG, "Shortcut no longer found");
+ mInjector.getBackgroundExecutor().execute(
+ () -> removeConversations(packageName, userId, Set.of(shortcutId)));
return null;
}
- String packageName = shortcutInfo.getPackage();
- String shortcutId = shortcutInfo.getId();
- int userId = shortcutInfo.getUserId();
int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
NotificationChannel parentChannel =
mNotificationManagerInternal.getNotificationChannel(packageName, uid,
@@ -1130,30 +1130,33 @@ public class DataManager {
public void onShortcutsRemoved(@NonNull String packageName,
@NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
mInjector.getBackgroundExecutor().execute(() -> {
- int uid = Process.INVALID_UID;
- try {
- uid = mContext.getPackageManager().getPackageUidAsUser(
- packageName, user.getIdentifier());
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "Package not found: " + packageName, e);
- }
- PackageData packageData = getPackage(packageName, user.getIdentifier());
- Set<String> shortcutIds = new HashSet<>();
+ HashSet<String> shortcutIds = new HashSet<>();
for (ShortcutInfo shortcutInfo : shortcuts) {
- if (packageData != null) {
- if (DEBUG) Log.d(TAG, "Deleting shortcut: " + shortcutInfo.getId());
- packageData.deleteDataForConversation(shortcutInfo.getId());
- }
shortcutIds.add(shortcutInfo.getId());
}
- if (uid != Process.INVALID_UID) {
- mNotificationManagerInternal.onConversationRemoved(
- packageName, uid, shortcutIds);
- }
+ removeConversations(packageName, user.getIdentifier(), shortcutIds);
});
}
}
+ private void removeConversations(
+ @NonNull String packageName, @NonNull int userId, @NonNull Set<String> shortcutIds) {
+ PackageData packageData = getPackage(packageName, userId);
+ if (packageData != null) {
+ for (String shortcutId : shortcutIds) {
+ if (DEBUG) Log.d(TAG, "Deleting shortcut: " + shortcutId);
+ packageData.deleteDataForConversation(shortcutId);
+ }
+ }
+ try {
+ int uid = mContext.getPackageManager().getPackageUidAsUser(
+ packageName, userId);
+ mNotificationManagerInternal.onConversationRemoved(packageName, uid, shortcutIds);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Package not found when removing conversation: " + packageName, e);
+ }
+ }
+
/** Listener for the notifications and their settings changes. */
private class NotificationListener extends NotificationListenerService {
@@ -1349,9 +1352,11 @@ public class DataManager {
}
private void updateConversationStoreThenNotifyListeners(ConversationStore cs,
- ConversationInfo modifiedConv, ShortcutInfo shortcutInfo) {
+ ConversationInfo modifiedConv, @NonNull ShortcutInfo shortcutInfo) {
cs.addOrUpdate(modifiedConv);
- ConversationChannel channel = getConversationChannel(shortcutInfo, modifiedConv);
+ ConversationChannel channel = getConversationChannel(
+ shortcutInfo, modifiedConv, shortcutInfo.getPackage(), shortcutInfo.getUserId(),
+ shortcutInfo.getId());
if (channel != null) {
notifyConversationsListeners(Arrays.asList(channel));
}
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index ca9ff6f15f3f..962a07ac6553 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -43,23 +43,23 @@ import static com.android.server.backup.testing.Utils.transferStreamedData;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.intThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.intThat;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import static org.robolectric.shadow.api.Shadow.extract;
@@ -2185,7 +2185,7 @@ public class KeyValueBackupTaskTest {
task.waitCancel();
reset(transportMock.transport);
taskFinished.block();
- verifyZeroInteractions(transportMock.transport);
+ verifyNoInteractions(transportMock.transport);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java
index 96302b954e75..299f15344dfa 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.when;
import android.database.Cursor;
import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
@@ -63,6 +64,7 @@ public final class ContactsQueryHelperTest {
private MatrixCursor mContactsLookupCursor;
private MatrixCursor mPhoneCursor;
private ContactsQueryHelper mHelper;
+ private ContactsContentProvider contentProvider;
@Before
public void setUp() {
@@ -73,7 +75,7 @@ public final class ContactsQueryHelperTest {
mPhoneCursor = new MatrixCursor(PHONE_COLUMNS);
MockContentResolver contentResolver = new MockContentResolver();
- ContactsContentProvider contentProvider = new ContactsContentProvider();
+ contentProvider = new ContactsContentProvider();
contentProvider.registerCursor(Contacts.CONTENT_URI, mContactsCursor);
contentProvider.registerCursor(
ContactsContract.PhoneLookup.CONTENT_FILTER_URI, mContactsLookupCursor);
@@ -89,6 +91,14 @@ public final class ContactsQueryHelperTest {
}
@Test
+ public void testQueryException_returnsFalse() {
+ contentProvider.setThrowException(true);
+
+ Uri contactUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, CONTACT_LOOKUP_KEY);
+ assertFalse(mHelper.query(contactUri.toString()));
+ }
+
+ @Test
public void testQueryWithUri() {
mContactsCursor.addRow(new Object[] {
/* id= */ 11, CONTACT_LOOKUP_KEY, /* starred= */ 1, /* hasPhoneNumber= */ 1,
@@ -168,10 +178,15 @@ public final class ContactsQueryHelperTest {
private class ContactsContentProvider extends MockContentProvider {
private Map<Uri, Cursor> mUriPrefixToCursorMap = new ArrayMap<>();
+ private boolean throwException = false;
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
+ if (throwException) {
+ throw new SQLiteException();
+ }
+
for (Uri prefixUri : mUriPrefixToCursorMap.keySet()) {
if (uri.isPathPrefixMatch(prefixUri)) {
return mUriPrefixToCursorMap.get(prefixUri);
@@ -180,6 +195,10 @@ public final class ContactsQueryHelperTest {
return mUriPrefixToCursorMap.get(uri);
}
+ public void setThrowException(boolean throwException) {
+ this.throwException = throwException;
+ }
+
private void registerCursor(Uri uriPrefix, Cursor cursor) {
mUriPrefixToCursorMap.put(uriPrefix, cursor);
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index b12e6ad563d1..4d177acb99fb 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -687,6 +687,34 @@ public final class DataManagerTest {
}
@Test
+ public void testGetConversation_unsyncedShortcut() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_PINNED);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+ assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID)).isNotNull();
+ assertThat(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+ .getConversationStore()
+ .getConversation(TEST_SHORTCUT_ID)).isNotNull();
+
+ when(mShortcutServiceInternal.getShortcuts(
+ anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(),
+ anyInt(), anyInt(), anyInt(), anyInt()))
+ .thenReturn(Collections.emptyList());
+ assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID)).isNull();
+
+ // Conversation is removed from store as there is no matching shortcut in ShortcutManager
+ assertThat(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+ .getConversationStore()
+ .getConversation(TEST_SHORTCUT_ID)).isNull();
+ verify(mNotificationManagerInternal)
+ .onConversationRemoved(TEST_PKG_NAME, TEST_PKG_UID, Set.of(TEST_SHORTCUT_ID));
+ }
+
+ @Test
public void testOnNotificationChannelModified() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index eed32d7d815c..bb20244aee91 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -249,6 +249,19 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
}
/**
+ * Ensures it updates recent tasks order when the last resumed activity changed.
+ */
+ @Test
+ public void testUpdateRecentTasksForTopResumed() {
+ spyOn(mSupervisor.mRecentTasks);
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task task = activity.getTask();
+
+ mAtm.setLastResumedActivityUncheckLocked(activity, "test");
+ verify(mSupervisor.mRecentTasks).add(eq(task));
+ }
+
+ /**
* Ensures that a trusted display can launch arbitrary activity and an untrusted display can't.
*/
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index a8e91980014e..995932c46201 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -197,27 +197,6 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
- public void testNotApplyStrategyToTranslucentActivitiesWithDifferentUid() {
- mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
- setUpDisplaySizeWithApp(2000, 1000);
- prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
- mActivity.info.setMinAspectRatio(1.2f);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
- // Translucent Activity
- final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
- .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
- .setMinAspectRatio(1.1f)
- .setMaxAspectRatio(3f)
- .build();
- doReturn(false).when(translucentActivity).fillsParent();
- mTask.addChild(translucentActivity);
- // We check bounds
- final Rect opaqueBounds = mActivity.getConfiguration().windowConfiguration.getBounds();
- final Rect translucentRequestedBounds = translucentActivity.getRequestedOverrideBounds();
- assertNotEquals(opaqueBounds, translucentRequestedBounds);
- }
-
- @Test
public void testApplyStrategyToMultipleTranslucentActivities() {
mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
setUpDisplaySizeWithApp(2000, 1000);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 6b3425cf095c..8244f9419b80 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -796,6 +796,38 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
}
@Test
+ public void testApplyTransaction_createTaskFragment_overrideBounds() {
+ final Task task = createTask(mDisplayContent);
+ final ActivityRecord activityAtBottom = createActivityRecord(task);
+ final int uid = Binder.getCallingUid();
+ activityAtBottom.info.applicationInfo.uid = uid;
+ activityAtBottom.getTask().effectiveUid = uid;
+ mTaskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .setFragmentToken(mFragmentToken)
+ .createActivityCount(1)
+ .build();
+ mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
+ final IBinder fragmentToken1 = new Binder();
+ final Rect bounds = new Rect(100, 100, 500, 1000);
+ final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
+ mOrganizerToken, fragmentToken1, activityAtBottom.token)
+ .setPairedActivityToken(activityAtBottom.token)
+ .setInitialBounds(bounds)
+ .build();
+ mTransaction.setTaskFragmentOrganizer(mIOrganizer);
+ mTransaction.createTaskFragment(params);
+ assertApplyTransactionAllowed(mTransaction);
+
+ // Successfully created a TaskFragment.
+ final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment(
+ fragmentToken1);
+ assertNotNull(taskFragment);
+ // The relative embedded bounds is updated to the initial requested bounds.
+ assertEquals(bounds, taskFragment.getRelativeEmbeddedBounds());
+ }
+
+ @Test
public void testApplyTransaction_createTaskFragment_withPairedActivityToken() {
final Task task = createTask(mDisplayContent);
final ActivityRecord activityAtBottom = createActivityRecord(task);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 3556ded23351..fd3776f828e5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -545,7 +545,12 @@ public class WindowStateTests extends WindowTestsBase {
win.applyWithNextDraw(t -> handledT[0] = t);
assertTrue(win.useBLASTSync());
final SurfaceControl.Transaction drawT = new StubTransaction();
+ final SurfaceControl.Transaction currT = win.getSyncTransaction();
+ clearInvocations(currT);
+ win.mWinAnimator.mLastHidden = true;
assertTrue(win.finishDrawing(drawT, Integer.MAX_VALUE));
+ // The draw transaction should be merged to current transaction even if the state is hidden.
+ verify(currT).merge(eq(drawT));
assertEquals(drawT, handledT[0]);
assertFalse(win.useBLASTSync());
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
index 47f87d6d75ff..573b3b695a90 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
@@ -102,6 +102,7 @@ public final class NotificationTest {
@After
public void tearDown() {
mNotificationManager.cancelAll();
+ mUiDevice.pressHome();
}
@Test