summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp14
-rw-r--r--Android.bp1
-rw-r--r--apex/jobscheduler/service/Android.bp1
-rw-r--r--apex/jobscheduler/service/aconfig/Android.bp12
-rw-r--r--apex/jobscheduler/service/aconfig/alarm.aconfig11
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java78
-rw-r--r--api/Android.bp1
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java2
-rw-r--r--cmds/am/src/com/android/commands/am/Instrument.java19
-rw-r--r--core/api/current.txt5
-rw-r--r--core/api/module-lib-current.txt2
-rw-r--r--core/api/system-current.txt44
-rw-r--r--core/java/android/animation/Animator.java15
-rw-r--r--core/java/android/app/ApplicationPackageManager.java14
-rw-r--r--core/java/android/app/OWNERS2
-rw-r--r--core/java/android/app/PictureInPictureUiState.java34
-rw-r--r--core/java/android/app/assist/AssistStructure.java23
-rw-r--r--core/java/android/app/wearable/IWearableSensingManager.aidl2
-rw-r--r--core/java/android/app/wearable/WearableSensingManager.java6
-rw-r--r--core/java/android/content/Context.java7
-rw-r--r--core/java/android/content/pm/SignedPackage.java14
-rw-r--r--core/java/android/content/pm/SignedPackageParcel.aidl2
-rw-r--r--core/java/android/content/pm/UserProperties.java2
-rw-r--r--core/java/android/content/pm/multiuser.aconfig14
-rw-r--r--core/java/android/credentials/GetCredentialResponse.java3
-rw-r--r--core/java/android/credentials/flags.aconfig16
-rw-r--r--core/java/android/credentials/selection/Constants.java7
-rw-r--r--core/java/android/credentials/selection/IntentFactory.java31
-rw-r--r--core/java/android/hardware/biometrics/BiometricFaceConstants.java20
-rw-r--r--core/java/android/hardware/biometrics/BiometricFingerprintConstants.java20
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java7
-rw-r--r--core/java/android/hardware/camera2/CameraExtensionCharacteristics.java5
-rw-r--r--core/java/android/hardware/face/FaceEnrollOptions.aidl19
-rw-r--r--core/java/android/hardware/face/FaceEnrollOptions.java259
-rw-r--r--core/java/android/hardware/face/FaceManager.java8
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl3
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintEnrollOptions.aidl19
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintEnrollOptions.java260
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java5
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl3
-rw-r--r--core/java/android/net/flags.aconfig8
-rw-r--r--core/java/android/os/VibrationEffect.java19
-rw-r--r--core/java/android/os/vibrator/PrebakedSegment.java8
-rw-r--r--core/java/android/os/vibrator/PrimitiveSegment.java20
-rw-r--r--core/java/android/os/vibrator/RampSegment.java15
-rw-r--r--core/java/android/os/vibrator/StepSegment.java21
-rw-r--r--core/java/android/os/vibrator/VibrationEffectSegment.java14
-rw-r--r--core/java/android/permission/flags.aconfig8
-rw-r--r--core/java/android/print/pdf/TEST_MAPPING7
-rw-r--r--core/java/android/provider/Settings.java18
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java2
-rw-r--r--core/java/android/service/notification/ZenPolicy.java4
-rw-r--r--core/java/android/service/notification/flags.aconfig2
-rw-r--r--core/java/android/service/voice/flags/flags.aconfig2
-rw-r--r--core/java/android/service/wearable/IWearableSensingService.aidl2
-rw-r--r--core/java/android/service/wearable/WearableSensingService.java10
-rw-r--r--core/java/android/text/Layout.java92
-rw-r--r--core/java/android/tracing/flags.aconfig1
-rw-r--r--core/java/android/view/BatchedInputEventReceiver.java13
-rw-r--r--core/java/android/view/HandwritingInitiator.java50
-rw-r--r--core/java/android/view/ISensitiveContentProtectionManager.aidl35
-rw-r--r--core/java/android/view/IWindowManager.aidl7
-rw-r--r--core/java/android/view/InputEventReceiver.java17
-rw-r--r--core/java/android/view/View.java25
-rw-r--r--core/java/android/view/autofill/AutofillManager.java87
-rw-r--r--core/java/android/view/autofill/IAutoFillManagerClient.aidl4
-rw-r--r--core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java23
-rw-r--r--core/java/android/view/inputmethod/InputBinding.java2
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java106
-rw-r--r--core/java/android/widget/Editor.java18
-rw-r--r--core/java/android/window/IGlobalDragListener.aidl (renamed from core/java/android/window/IUnhandledDragListener.aidl)11
-rw-r--r--core/java/com/android/internal/app/SetScreenLockDialogActivity.java160
-rw-r--r--core/java/com/android/internal/inputmethod/IBooleanListener.aidl25
-rw-r--r--core/java/com/android/internal/protolog/common/LogLevel.java26
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl8
-rw-r--r--core/java/com/android/internal/widget/CallLayout.java47
-rw-r--r--core/jni/android_view_InputEventReceiver.cpp28
-rw-r--r--core/jni/android_view_InputEventSender.cpp2
-rw-r--r--core/jni/android_view_InputQueue.cpp2
-rw-r--r--core/jni/android_view_KeyCharacterMap.cpp8
-rw-r--r--core/jni/android_view_KeyEvent.cpp24
-rw-r--r--core/jni/android_view_KeyEvent.h9
-rw-r--r--core/jni/android_view_MotionEvent.cpp41
-rw-r--r--core/jni/android_view_MotionEvent.h11
-rw-r--r--core/jni/android_view_MotionPredictor.cpp3
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--core/res/res/values-af/strings.xml18
-rw-r--r--core/res/res/values-am/strings.xml18
-rw-r--r--core/res/res/values-ar/strings.xml18
-rw-r--r--core/res/res/values-as/strings.xml18
-rw-r--r--core/res/res/values-az/strings.xml18
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml18
-rw-r--r--core/res/res/values-be/strings.xml18
-rw-r--r--core/res/res/values-bg/strings.xml18
-rw-r--r--core/res/res/values-bn/strings.xml18
-rw-r--r--core/res/res/values-bs/strings.xml18
-rw-r--r--core/res/res/values-ca/strings.xml18
-rw-r--r--core/res/res/values-cs/strings.xml18
-rw-r--r--core/res/res/values-da/strings.xml18
-rw-r--r--core/res/res/values-de/strings.xml18
-rw-r--r--core/res/res/values-el/strings.xml18
-rw-r--r--core/res/res/values-en-rAU/strings.xml18
-rw-r--r--core/res/res/values-en-rCA/strings.xml18
-rw-r--r--core/res/res/values-en-rGB/strings.xml18
-rw-r--r--core/res/res/values-en-rIN/strings.xml18
-rw-r--r--core/res/res/values-en-rXC/strings.xml18
-rw-r--r--core/res/res/values-es-rUS/strings.xml18
-rw-r--r--core/res/res/values-es/strings.xml18
-rw-r--r--core/res/res/values-et/strings.xml18
-rw-r--r--core/res/res/values-eu/strings.xml18
-rw-r--r--core/res/res/values-fa/strings.xml18
-rw-r--r--core/res/res/values-fi/strings.xml18
-rw-r--r--core/res/res/values-fr-rCA/strings.xml18
-rw-r--r--core/res/res/values-fr/strings.xml26
-rw-r--r--core/res/res/values-gl/strings.xml20
-rw-r--r--core/res/res/values-gu/strings.xml18
-rw-r--r--core/res/res/values-hi/strings.xml20
-rw-r--r--core/res/res/values-hr/strings.xml18
-rw-r--r--core/res/res/values-hu/strings.xml18
-rw-r--r--core/res/res/values-hy/strings.xml22
-rw-r--r--core/res/res/values-in/strings.xml18
-rw-r--r--core/res/res/values-is/strings.xml18
-rw-r--r--core/res/res/values-it/strings.xml18
-rw-r--r--core/res/res/values-iw/strings.xml18
-rw-r--r--core/res/res/values-ja/strings.xml18
-rw-r--r--core/res/res/values-ka/strings.xml18
-rw-r--r--core/res/res/values-kk/strings.xml18
-rw-r--r--core/res/res/values-km/strings.xml18
-rw-r--r--core/res/res/values-kn/strings.xml18
-rw-r--r--core/res/res/values-ko/strings.xml18
-rw-r--r--core/res/res/values-ky/strings.xml18
-rw-r--r--core/res/res/values-lo/strings.xml18
-rw-r--r--core/res/res/values-lt/strings.xml18
-rw-r--r--core/res/res/values-lv/strings.xml26
-rw-r--r--core/res/res/values-mk/strings.xml18
-rw-r--r--core/res/res/values-ml/strings.xml18
-rw-r--r--core/res/res/values-mn/strings.xml18
-rw-r--r--core/res/res/values-mr/strings.xml46
-rw-r--r--core/res/res/values-ms/strings.xml18
-rw-r--r--core/res/res/values-my/strings.xml18
-rw-r--r--core/res/res/values-nb/strings.xml18
-rw-r--r--core/res/res/values-ne/strings.xml18
-rw-r--r--core/res/res/values-nl/strings.xml18
-rw-r--r--core/res/res/values-or/strings.xml18
-rw-r--r--core/res/res/values-pa/strings.xml18
-rw-r--r--core/res/res/values-pl/strings.xml18
-rw-r--r--core/res/res/values-pt-rBR/strings.xml18
-rw-r--r--core/res/res/values-pt-rPT/strings.xml18
-rw-r--r--core/res/res/values-pt/strings.xml18
-rw-r--r--core/res/res/values-ro/strings.xml20
-rw-r--r--core/res/res/values-ru/strings.xml18
-rw-r--r--core/res/res/values-si/strings.xml18
-rw-r--r--core/res/res/values-sk/strings.xml18
-rw-r--r--core/res/res/values-sl/strings.xml18
-rw-r--r--core/res/res/values-sq/strings.xml18
-rw-r--r--core/res/res/values-sr/strings.xml18
-rw-r--r--core/res/res/values-sv/strings.xml18
-rw-r--r--core/res/res/values-sw/strings.xml18
-rw-r--r--core/res/res/values-ta/strings.xml18
-rw-r--r--core/res/res/values-te/strings.xml18
-rw-r--r--core/res/res/values-th/strings.xml18
-rw-r--r--core/res/res/values-tl/strings.xml18
-rw-r--r--core/res/res/values-tr/strings.xml18
-rw-r--r--core/res/res/values-uk/strings.xml18
-rw-r--r--core/res/res/values-ur/strings.xml18
-rw-r--r--core/res/res/values-uz/strings.xml18
-rw-r--r--core/res/res/values-vi/strings.xml18
-rw-r--r--core/res/res/values-zh-rCN/strings.xml18
-rw-r--r--core/res/res/values-zh-rHK/strings.xml18
-rw-r--r--core/res/res/values-zh-rTW/strings.xml18
-rw-r--r--core/res/res/values-zu/strings.xml18
-rw-r--r--core/res/res/values/strings.xml7
-rw-r--r--core/res/res/values/symbols.xml6
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java2
-rw-r--r--core/tests/coretests/src/android/hardware/face/FaceManagerTest.java6
-rw-r--r--core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java6
-rw-r--r--core/tests/coretests/src/android/text/LayoutTest.java275
-rw-r--r--core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java7
-rw-r--r--core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java21
-rw-r--r--core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java31
-rw-r--r--core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java31
-rw-r--r--graphics/java/android/graphics/Canvas.java14
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml5
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java114
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java113
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt62
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java86
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/UnhandledDragController.kt)49
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt86
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt139
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/GlobalDragListenerTest.kt (renamed from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/UnhandledDragControllerTest.kt)58
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java32
-rw-r--r--libs/hwui/Android.bp41
-rw-r--r--libs/hwui/ProfileData.cpp2
-rw-r--r--libs/hwui/hwui/DrawTextFunctor.h1
-rw-r--r--libs/hwui/jni/android_graphics_Canvas.cpp5
-rw-r--r--libs/hwui/platform/android/thread/ThreadBase.h (renamed from libs/hwui/thread/ThreadBase.h)6
-rw-r--r--libs/hwui/platform/host/ProfileDataContainer.cpp40
-rw-r--r--libs/hwui/platform/host/Readback.cpp50
-rw-r--r--libs/hwui/platform/host/WebViewFunctorManager.cpp75
-rw-r--r--libs/hwui/platform/host/renderthread/CacheManager.cpp64
-rw-r--r--libs/hwui/platform/host/renderthread/RenderThread.cpp141
-rw-r--r--libs/hwui/platform/host/thread/ThreadBase.h76
-rw-r--r--libs/hwui/private/hwui/WebViewFunctor.h8
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp3
-rw-r--r--libs/hwui/renderthread/RenderThread.h3
-rw-r--r--media/java/android/media/FadeManagerConfiguration.java30
-rw-r--r--media/java/android/media/browse/MediaBrowser.java34
-rw-r--r--media/java/android/media/flags/media_better_together.aconfig7
-rw-r--r--media/java/android/media/midi/package.html2
-rw-r--r--media/java/android/media/tv/SignalingDataResponse.java4
-rwxr-xr-xmedia/java/android/media/tv/interactive/TvInteractiveAppService.java8
-rwxr-xr-xmedia/java/android/media/tv/interactive/TvInteractiveAppView.java3
-rw-r--r--media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl7
-rw-r--r--media/java/android/service/media/MediaBrowserService.java96
-rw-r--r--native/android/input.cpp12
-rw-r--r--nfc/api/current.txt2
-rw-r--r--nfc/java/android/nfc/NfcAdapter.java6
-rw-r--r--nfc/java/android/nfc/cardemulation/ApduServiceInfo.java28
-rw-r--r--packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java4
-rw-r--r--packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java8
-rw-r--r--packages/CompanionDeviceManager/res/values-nb/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values/styles.xml4
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt5
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt17
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt8
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt6
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt121
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt1
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt25
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt1
-rw-r--r--packages/PackageInstaller/res/values-fr/strings.xml2
-rw-r--r--packages/PackageInstaller/res/values-pt-rBR/strings.xml2
-rw-r--r--packages/PackageInstaller/res/values-pt/strings.xml2
-rw-r--r--packages/PrintRecommendationService/res/values/strings.xml1
-rw-r--r--packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java9
-rw-r--r--packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java166
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java1
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt8
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt6
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt4
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownBoxPageProvider.kt (renamed from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt)55
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownCheckBoxProvider.kt134
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt75
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt10
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/DropdownTextBox.kt (renamed from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt)87
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBox.kt69
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBox.kt145
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt164
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt17
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBoxTest.kt109
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBoxTest.kt145
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt95
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt115
-rw-r--r--packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/SemanticsMatcher.kt26
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt3
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt1
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt4
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt25
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt24
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt13
-rw-r--r--packages/SettingsLib/res/drawable/ic_bt_le_audio_sharing.xml87
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-az/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-en-rXC/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-mn/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ms/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-si/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java70
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java81
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java39
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java40
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java66
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtils.java113
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java129
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java18
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/OWNERS3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java17
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt18
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt93
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/OWNERS5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt53
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt15
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt23
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt86
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiver.kt (renamed from packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerIntentsReceiver.kt)46
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioManagerEvent.kt37
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioStream.kt2
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/OWNERS1
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt42
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt6
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt12
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt139
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerEventsReceiver.kt (renamed from packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerIntentsReceiver.kt)16
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java15
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtilsTest.java88
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java50
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java4
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING11
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/res/values-sv/strings.xml2
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig20
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/anc/AncModule.kt21
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt5
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt1
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt5
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt16
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt288
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt16
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt53
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt110
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt81
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt1
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt116
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt66
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt41
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt54
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt29
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt43
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt72
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt)34
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesResourceRepositoryTest.kt45
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt97
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt28
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt147
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt113
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryTest.kt183
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryTest.kt58
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorTest.kt154
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorTest.kt80
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt33
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt33
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt306
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt)2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/OWNERS1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt184
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt114
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt89
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt101
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt40
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt60
-rw-r--r--packages/SystemUI/res/drawable/ic_noise_aware.xml26
-rw-r--r--packages/SystemUI/res/drawable/media_squiggly_progress.xml2
-rw-r--r--packages/SystemUI/res/drawable/qs_media_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/qs_media_light_source.xml2
-rw-r--r--packages/SystemUI/res/layout/anc_slice.xml20
-rw-r--r--packages/SystemUI/res/layout/media_carousel.xml4
-rw-r--r--packages/SystemUI/res/layout/scene_window_root.xml2
-rw-r--r--packages/SystemUI/res/layout/screen_share_dialog.xml4
-rw-r--r--packages/SystemUI/res/values-af/strings.xml12
-rw-r--r--packages/SystemUI/res/values-am/strings.xml9
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml9
-rw-r--r--packages/SystemUI/res/values-as/strings.xml9
-rw-r--r--packages/SystemUI/res/values-az/strings.xml9
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml3
-rw-r--r--packages/SystemUI/res/values-be/strings.xml6
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml14
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml12
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml9
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml3
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml3
-rw-r--r--packages/SystemUI/res/values-da/strings.xml12
-rw-r--r--packages/SystemUI/res/values-de/strings.xml12
-rw-r--r--packages/SystemUI/res/values-el/strings.xml9
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml12
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml3
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml12
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml12
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml3
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml6
-rw-r--r--packages/SystemUI/res/values-es/strings.xml3
-rw-r--r--packages/SystemUI/res/values-et/strings.xml9
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml12
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml11
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml12
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml12
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml5
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml12
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml12
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml12
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml9
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml9
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml12
-rw-r--r--packages/SystemUI/res/values-in/strings.xml12
-rw-r--r--packages/SystemUI/res/values-is/strings.xml12
-rw-r--r--packages/SystemUI/res/values-it/strings.xml9
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml9
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml12
-rw-r--r--packages/SystemUI/res/values-km/strings.xml9
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml9
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml12
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml12
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml3
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml3
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml12
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml12
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml3
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml12
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml15
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml3
-rw-r--r--packages/SystemUI/res/values-my/strings.xml12
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml9
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml3
-rw-r--r--packages/SystemUI/res/values-or/strings.xml12
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml12
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml12
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml12
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml9
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml12
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml12
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml12
-rw-r--r--packages/SystemUI/res/values-si/strings.xml12
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml12
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml9
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml12
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml3
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml12
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml9
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml12
-rw-r--r--packages/SystemUI/res/values-te/strings.xml9
-rw-r--r--packages/SystemUI/res/values-th/strings.xml3
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml3
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml9
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml12
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml9
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml12
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml12
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml12
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml12
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml14
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml12
-rw-r--r--packages/SystemUI/res/values/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml5
-rw-r--r--packages/SystemUI/res/values/styles.xml13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SysUICutoutProvider.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/complication/DreamMediaEntryComplication.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/qualifiers/NotifInflation.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt112
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/DisplayExtensions.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepository.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractor.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/NavbarOrientationTrackingLog.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatest.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilter.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt)35
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt)40
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilter.kt)10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt)6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutLogger.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaBrowserFactory.java (renamed from packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaBrowserFactory.java)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowser.java (renamed from packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowser.java)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserFactory.java (renamed from packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserFactory.java)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserLogger.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaData.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaDataProvider.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandler.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MediaColorSchemes.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaColorSchemes.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandler.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MetadataAnimationHandler.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt)6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt)17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java)30
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt)5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManager.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt)3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt)13
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/IlluminationDrawable.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/LightSourceDrawable.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgress.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/GutsViewHolder.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/GutsViewHolder.kt)10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt)3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt)11
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaScrollView.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaViewHolder.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt)3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt)5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/LocalMediaManagerFactory.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavbarOrientationTrackingLogger.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesRepository.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt133
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepository.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractor.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractor.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java77
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationRepository.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/OWNERS8
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/AncModule.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt108
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/CastVolumeInteractor.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractor.kt98
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/model/SliderType.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderImplTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysUICutoutProviderTest.kt169
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryTest.kt69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorTest.kt67
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/MediaTestUtils.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java)6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt)10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt)60
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt)97
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilterTest.kt)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt)6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt)10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/shared/SmartspaceMediaDataTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt)6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/AnimationBindHandlerTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt)6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MetadataAnimationHandlerTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt)6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt)14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt)28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt)7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt)7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/SquigglyProgressTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/MediaViewHolderTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt202
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt48
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/FaceHelpMessageDeferralKosmos.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/classifier/domain/interactor/FalsingInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractorKosmos.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryKosmos.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt)2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerV2Kosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeDefaultTilesRepository.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt12
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakePrivacyChipRepository.kt55
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorKosmos.kt35
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorKosmos.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt28
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HeadsUpNotificationInteractorKosmos.kt26
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt25
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/NextAlarmControllerKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt13
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeLocalMediaRepository.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/FakeSliceFactory.kt51
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt33
-rw-r--r--packages/VpnDialogs/res/values-mr/strings.xml2
-rw-r--r--packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java14
-rw-r--r--ravenwood/ravenwood-annotation-allowed-classes.txt1
-rw-r--r--services/accessibility/accessibility.aconfig27
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java127
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java49
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java168
-rw-r--r--services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java60
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java23
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java13
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java96
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/FillUi.java4
-rw-r--r--services/backup/flags.aconfig9
-rw-r--r--services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java4
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java181
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/IntentResolver.java16
-rw-r--r--services/core/java/com/android/server/SensitiveContentProtectionManagerService.java104
-rw-r--r--services/core/java/com/android/server/am/LmkdStatsReporter.java11
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java11
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java53
-rw-r--r--services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java24
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricNotificationLogger.java104
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java23
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java12
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricLogger.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java52
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/EnrollClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java29
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceShellCommand.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java17
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java27
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java19
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java9
-rw-r--r--services/core/java/com/android/server/display/BrightnessThrottler.java61
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java29
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java4
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java7
-rw-r--r--services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java6
-rw-r--r--services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java136
-rw-r--r--services/core/java/com/android/server/display/config/SensorData.java30
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java10
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig8
-rw-r--r--services/core/java/com/android/server/display/utils/SensorUtils.java15
-rw-r--r--services/core/java/com/android/server/input/debug/FocusEventDebugView.java10
-rw-r--r--services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java13
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java35
-rw-r--r--services/core/java/com/android/server/inputmethod/ZeroJankProxy.java14
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java9
-rw-r--r--services/core/java/com/android/server/media/MediaSession2Record.java22
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java29
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecordImpl.java7
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java51
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java57
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java44
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig10
-rw-r--r--services/core/java/com/android/server/pdb/PersistentDataBlockService.java5
-rw-r--r--services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java99
-rw-r--r--services/core/java/com/android/server/pm/PackageArchiver.java25
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java23
-rw-r--r--services/core/java/com/android/server/pm/UserTypeFactory.java6
-rw-r--r--services/core/java/com/android/server/pm/VerifyingSession.java8
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java2
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsImpl.java29
-rw-r--r--services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java9
-rw-r--r--services/core/java/com/android/server/vibrator/HalVibration.java17
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationScaler.java9
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationStepConductor.java3
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java19
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java5
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java5
-rw-r--r--services/core/java/com/android/server/wearable/RemoteWearableSensingService.java58
-rw-r--r--services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java6
-rw-r--r--services/core/java/com/android/server/wearable/WearableSensingManagerService.java6
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java26
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java21
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java20
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java13
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java39
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java55
-rw-r--r--services/core/java/com/android/server/wm/SensitiveContentPackages.java78
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java13
-rw-r--r--services/core/java/com/android/server/wm/Transition.java7
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java63
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java23
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java22
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java16
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp26
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd3
-rw-r--r--services/core/xsd/display-device-config/schema/current.txt2
-rw-r--r--services/credentials/java/com/android/server/credentials/CreateRequestSession.java5
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerUi.java45
-rw-r--r--services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java7
-rw-r--r--services/credentials/java/com/android/server/credentials/GetRequestSession.java6
-rw-r--r--services/credentials/java/com/android/server/credentials/MetricUtilities.java2
-rw-r--r--services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java5
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderGetSession.java49
-rw-r--r--services/midi/java/com/android/server/midi/MidiService.java6
-rw-r--r--services/tests/InputMethodSystemServerTests/Android.bp1
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java44
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java67
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java15
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java91
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java145
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java (renamed from services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java)10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java121
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java144
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING12
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java57
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt317
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java381
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/BiometricNotificationLoggerTest.java157
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java50
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java64
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java73
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java50
-rw-r--r--services/tests/wmtests/AndroidTest.xml7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java27
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java147
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java134
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java64
-rw-r--r--telecomm/java/android/telecom/DisconnectCause.java116
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java8
-rw-r--r--telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl1
-rw-r--r--telephony/java/android/telephony/data/QualifiedNetworksService.java57
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt5
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml16
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java25
-rw-r--r--tests/vcn/Android.bp2
-rw-r--r--tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java19
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt1
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/LogLevel.kt38
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt19
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt1
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt1
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt1
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt1
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt1
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt1
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt1
-rw-r--r--wifi/TEST_MAPPING4
999 files changed, 18064 insertions, 5628 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 14658e5d9e4f..20471682dd2e 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -910,6 +910,8 @@ java_aconfig_library {
aconfig_declarations {
name: "android.service.notification.flags-aconfig",
package: "android.service.notification",
+ exportable: true,
+ container: "system",
srcs: ["core/java/android/service/notification/flags.aconfig"],
}
@@ -919,6 +921,18 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.service.notification.flags-aconfig-export-java",
+ aconfig_declarations: "android.service.notification.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ mode: "exported",
+ min_sdk_version: "30",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.extservices",
+ ],
+}
+
// Smartspace
aconfig_declarations {
name: "android.app.smartspace.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index 870df5a5723e..019bf6508774 100644
--- a/Android.bp
+++ b/Android.bp
@@ -508,7 +508,6 @@ java_library {
lint: {
baseline_filename: "lint-baseline.xml",
},
- // For jarjar repackaging
jarjar_prefix: "com.android.internal.hidden_from_bootclasspath",
}
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index 8b55e071e715..558629537253 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -28,6 +28,7 @@ java_library {
static_libs: [
"modules-utils-fastxmlserializer",
+ "service-jobscheduler-alarm.flags-aconfig-java",
"service-jobscheduler-job.flags-aconfig-java",
],
diff --git a/apex/jobscheduler/service/aconfig/Android.bp b/apex/jobscheduler/service/aconfig/Android.bp
index 3ba7aa201d27..4db39dc1976b 100644
--- a/apex/jobscheduler/service/aconfig/Android.bp
+++ b/apex/jobscheduler/service/aconfig/Android.bp
@@ -27,3 +27,15 @@ java_aconfig_library {
aconfig_declarations: "service-job.flags-aconfig",
visibility: ["//frameworks/base:__subpackages__"],
}
+
+// Alarm
+aconfig_declarations {
+ name: "alarm_flags",
+ package: "com.android.server.alarm",
+ srcs: ["alarm.aconfig"],
+}
+
+java_aconfig_library {
+ name: "service-jobscheduler-alarm.flags-aconfig-java",
+ aconfig_declarations: "alarm_flags",
+}
diff --git a/apex/jobscheduler/service/aconfig/alarm.aconfig b/apex/jobscheduler/service/aconfig/alarm.aconfig
new file mode 100644
index 000000000000..3b9b4e70b310
--- /dev/null
+++ b/apex/jobscheduler/service/aconfig/alarm.aconfig
@@ -0,0 +1,11 @@
+package: "com.android.server.alarm"
+
+flag {
+ name: "use_frozen_state_to_drop_listener_alarms"
+ namespace: "backstage_power"
+ description: "Use frozen state callback to drop listener alarms for cached apps"
+ bug: "324470945"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 39de0af3c8d0..e728a2c55765 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.alarm;
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
@@ -75,6 +76,7 @@ import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.AlarmManager;
@@ -103,6 +105,7 @@ import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -145,6 +148,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.LocalLog;
@@ -293,6 +297,7 @@ public class AlarmManagerService extends SystemService {
private final Injector mInjector;
int mBroadcastRefCount = 0;
+ boolean mUseFrozenStateToDropListenerAlarms;
MetricsHelper mMetricsHelper;
PowerManager.WakeLock mWakeLock;
SparseIntArray mAlarmsPerUid = new SparseIntArray();
@@ -1856,15 +1861,47 @@ public class AlarmManagerService extends SystemService {
@Override
public void onStart() {
mInjector.init();
+ mHandler = new AlarmHandler();
+
mOptsWithFgs.setPendingIntentBackgroundActivityLaunchAllowed(false);
mOptsWithFgsForAlarmClock.setPendingIntentBackgroundActivityLaunchAllowed(false);
mOptsWithoutFgs.setPendingIntentBackgroundActivityLaunchAllowed(false);
mOptsTimeBroadcast.setPendingIntentBackgroundActivityLaunchAllowed(false);
mActivityOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false);
mBroadcastOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false);
+
mMetricsHelper = new MetricsHelper(getContext(), mLock);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mUseFrozenStateToDropListenerAlarms = Flags.useFrozenStateToDropListenerAlarms();
+ if (mUseFrozenStateToDropListenerAlarms) {
+ final ActivityManager.UidFrozenStateChangedCallback callback = (uids, frozenStates) -> {
+ final int size = frozenStates.length;
+ if (uids.length != size) {
+ Slog.wtf(TAG, "Got different length arrays in frozen state callback!"
+ + " uids.length: " + uids.length + " frozenStates.length: " + size);
+ // Cannot process received data in any meaningful way.
+ return;
+ }
+ final IntArray affectedUids = new IntArray();
+ for (int i = 0; i < size; i++) {
+ if (frozenStates[i] != UID_FROZEN_STATE_FROZEN) {
+ continue;
+ }
+ if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED,
+ uids[i])) {
+ continue;
+ }
+ affectedUids.add(uids[i]);
+ }
+ if (affectedUids.size() > 0) {
+ removeExactListenerAlarms(affectedUids.toArray());
+ }
+ };
+ final ActivityManager am = getContext().getSystemService(ActivityManager.class);
+ am.registerUidFrozenStateChangedCallback(new HandlerExecutor(mHandler), callback);
+ }
+
mListenerDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
@@ -1880,7 +1917,6 @@ public class AlarmManagerService extends SystemService {
};
synchronized (mLock) {
- mHandler = new AlarmHandler();
mConstants = new Constants(mHandler);
mAlarmStore = new LazyAlarmStore();
@@ -1960,6 +1996,21 @@ public class AlarmManagerService extends SystemService {
publishBinderService(Context.ALARM_SERVICE, mService);
}
+ private void removeExactListenerAlarms(int... whichUids) {
+ synchronized (mLock) {
+ removeAlarmsInternalLocked(a -> {
+ if (!ArrayUtils.contains(whichUids, a.uid) || a.listener == null
+ || a.windowLength != 0) {
+ return false;
+ }
+ Slog.w(TAG, "Alarm " + a.listenerTag + " being removed for "
+ + UserHandle.formatUid(a.uid) + ":" + a.packageName
+ + " because the app got frozen");
+ return true;
+ }, REMOVE_REASON_LISTENER_CACHED);
+ }
+ }
+
void refreshExactAlarmCandidates() {
final String[] candidates = mLocalPermissionManager.getAppOpPermissionPackages(
Manifest.permission.SCHEDULE_EXACT_ALARM);
@@ -3074,6 +3125,14 @@ public class AlarmManagerService extends SystemService {
mConstants.dump(pw);
pw.println();
+ pw.println("Feature Flags:");
+ pw.increaseIndent();
+ pw.print(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS,
+ mUseFrozenStateToDropListenerAlarms);
+ pw.decreaseIndent();
+ pw.println();
+ pw.println();
+
if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON) {
pw.println("TARE details:");
pw.increaseIndent();
@@ -4959,18 +5018,7 @@ public class AlarmManagerService extends SystemService {
break;
case REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED:
uid = (Integer) msg.obj;
- synchronized (mLock) {
- removeAlarmsInternalLocked(a -> {
- if (a.uid != uid || a.listener == null || a.windowLength != 0) {
- return false;
- }
- // TODO (b/265195908): Change to .w once we have some data on breakages.
- Slog.wtf(TAG, "Alarm " + a.listenerTag + " being removed for "
- + UserHandle.formatUid(a.uid) + ":" + a.packageName
- + " because the app went into cached state");
- return true;
- }, REMOVE_REASON_LISTENER_CACHED);
- }
+ removeExactListenerAlarms(uid);
break;
default:
// nope, just ignore it
@@ -5322,6 +5370,10 @@ public class AlarmManagerService extends SystemService {
@Override
public void handleUidCachedChanged(int uid, boolean cached) {
+ if (mUseFrozenStateToDropListenerAlarms) {
+ // Use ActivityManager#UidFrozenStateChangedCallback instead.
+ return;
+ }
if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, uid)) {
return;
}
diff --git a/api/Android.bp b/api/Android.bp
index eeb76fbd81ef..8e063667826c 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -113,6 +113,7 @@ combined_apis {
"framework-nfc",
"framework-ondevicepersonalization",
"framework-pdf",
+ "framework-pdf-v",
"framework-permission",
"framework-permission-s",
"framework-profiling",
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index d79131ca5d7c..3b16cab06bab 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -183,6 +183,8 @@ public class Am extends BaseCommand {
instrument.disableTestApiChecks = false;
} else if (opt.equals("--no-isolated-storage")) {
instrument.disableIsolatedStorage = true;
+ } else if (opt.equals("--no-logcat")) {
+ instrument.captureLogcat = false;
} else if (opt.equals("--user")) {
instrument.userId = parseUserArg(nextArgRequired());
} else if (opt.equals("--abi")) {
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index e60593e8b633..e0d949e04a92 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -85,6 +85,7 @@ public class Instrument {
public String profileFile = null;
public boolean wait = false;
public boolean rawMode = false;
+ public boolean captureLogcat = true;
boolean protoStd = false; // write proto to stdout
boolean protoFile = false; // write proto to a file
String logPath = null;
@@ -266,16 +267,18 @@ public class Instrument {
proto.write(InstrumentationData.TestStatus.RESULT_CODE, resultCode);
writeBundle(proto, InstrumentationData.TestStatus.RESULTS, results);
- if (resultCode == STATUS_TEST_STARTED) {
- // Logcat -T takes wall clock time (!?)
- mTestStartMs = System.currentTimeMillis();
- } else {
- if (mTestStartMs > 0) {
- proto.write(InstrumentationData.TestStatus.LOGCAT, readLogcat(mTestStartMs));
+ if (captureLogcat) {
+ if (resultCode == STATUS_TEST_STARTED) {
+ // Logcat -T takes wall clock time (!?)
+ mTestStartMs = System.currentTimeMillis();
+ } else {
+ if (mTestStartMs > 0) {
+ proto.write(InstrumentationData.TestStatus.LOGCAT,
+ readLogcat(mTestStartMs));
+ }
+ mTestStartMs = 0;
}
- mTestStartMs = 0;
}
-
proto.end(testStatusToken);
outputProto(proto);
diff --git a/core/api/current.txt b/core/api/current.txt
index 7dc15fbfbcc2..3fd67db95e1b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7239,8 +7239,8 @@ package android.app {
public final class PictureInPictureUiState implements android.os.Parcelable {
method public int describeContents();
- method @FlaggedApi("android.app.enable_pip_ui_state_callback_on_entering") public boolean isEnteringPip();
method public boolean isStashed();
+ method @FlaggedApi("android.app.enable_pip_ui_state_callback_on_entering") public boolean isTransitioningToPip();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.PictureInPictureUiState> CREATOR;
}
@@ -56278,7 +56278,8 @@ package android.view.inputmethod {
public final class InputMethodManager {
method public boolean acceptStylusHandwritingDelegation(@NonNull android.view.View);
method public boolean acceptStylusHandwritingDelegation(@NonNull android.view.View, @NonNull String);
- method @FlaggedApi("android.view.inputmethod.home_screen_handwriting_delegator") public boolean acceptStylusHandwritingDelegation(@NonNull android.view.View, @NonNull String, int);
+ method @FlaggedApi("android.view.inputmethod.use_zero_jank_proxy") public void acceptStylusHandwritingDelegation(@NonNull android.view.View, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @FlaggedApi("android.view.inputmethod.home_screen_handwriting_delegator") public void acceptStylusHandwritingDelegation(@NonNull android.view.View, @NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void dispatchKeyEventFromInputMethod(@Nullable android.view.View, @NonNull android.view.KeyEvent);
method public void displayCompletions(android.view.View, android.view.inputmethod.CompletionInfo[]);
method @Nullable public android.view.inputmethod.InputMethodInfo getCurrentInputMethodInfo();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 7c4df280eaa7..9c1a8e854e92 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -135,7 +135,7 @@ package android.content.pm {
@FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public class SignedPackage {
method @NonNull public byte[] getCertificateDigest();
- method @NonNull public String getPkgName();
+ method @NonNull public String getPackageName();
}
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 627b70355f50..cc7d97a6ee50 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3339,9 +3339,9 @@ package android.app.wearable {
public class WearableSensingManager {
method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @Nullable public static android.app.wearable.WearableSensingDataRequest getDataRequestFromIntent(@NonNull android.content.Intent);
+ method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideConnection(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideData(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideDataStream(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideWearableConnection(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void registerDataRequestObserver(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void startHotwordRecognition(@Nullable android.content.ComponentName, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void stopHotwordRecognition(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
@@ -7199,15 +7199,15 @@ package android.media {
@FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") public final class FadeManagerConfiguration implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.media.AudioAttributes> getAudioAttributesWithVolumeShaperConfigs();
- method public static long getDefaultFadeInDurationMillis();
- method public static long getDefaultFadeOutDurationMillis();
- method public long getFadeInDelayForOffenders();
- method public long getFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes);
- method public long getFadeInDurationForUsage(int);
+ method @IntRange(from=1) public static long getDefaultFadeInDurationMillis();
+ method @IntRange(from=1) public static long getDefaultFadeOutDurationMillis();
+ method @IntRange(from=0) public long getFadeInDelayForOffenders();
+ method @IntRange(from=0) public long getFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method @IntRange(from=0) public long getFadeInDurationForUsage(int);
method @Nullable public android.media.VolumeShaper.Configuration getFadeInVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes);
method @Nullable public android.media.VolumeShaper.Configuration getFadeInVolumeShaperConfigForUsage(int);
- method public long getFadeOutDurationForAudioAttributes(@NonNull android.media.AudioAttributes);
- method public long getFadeOutDurationForUsage(int);
+ method @IntRange(from=0) public long getFadeOutDurationForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method @IntRange(from=0) public long getFadeOutDurationForUsage(int);
method @Nullable public android.media.VolumeShaper.Configuration getFadeOutVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes);
method @Nullable public android.media.VolumeShaper.Configuration getFadeOutVolumeShaperConfigForUsage(int);
method public int getFadeState();
@@ -7233,7 +7233,7 @@ package android.media {
public static final class FadeManagerConfiguration.Builder {
ctor public FadeManagerConfiguration.Builder();
- ctor public FadeManagerConfiguration.Builder(long, long);
+ ctor public FadeManagerConfiguration.Builder(@IntRange(from=1) long, @IntRange(from=1) long);
ctor public FadeManagerConfiguration.Builder(@NonNull android.media.FadeManagerConfiguration);
method @NonNull public android.media.FadeManagerConfiguration.Builder addFadeableUsage(int);
method @NonNull public android.media.FadeManagerConfiguration.Builder addUnfadeableAudioAttributes(@NonNull android.media.AudioAttributes);
@@ -7244,13 +7244,13 @@ package android.media {
method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableAudioAttributes();
method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableContentTypes();
method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableUids();
- method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDelayForOffenders(long);
- method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes, long);
- method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForUsage(int, long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDelayForOffenders(@IntRange(from=0) long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes, @IntRange(from=0) long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForUsage(int, @IntRange(from=0) long);
method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes, @Nullable android.media.VolumeShaper.Configuration);
method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInVolumeShaperConfigForUsage(int, @Nullable android.media.VolumeShaper.Configuration);
- method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutDurationForAudioAttributes(@NonNull android.media.AudioAttributes, long);
- method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutDurationForUsage(int, long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutDurationForAudioAttributes(@NonNull android.media.AudioAttributes, @IntRange(from=0) long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutDurationForUsage(int, @IntRange(from=0) long);
method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes, @Nullable android.media.VolumeShaper.Configuration);
method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutVolumeShaperConfigForUsage(int, @Nullable android.media.VolumeShaper.Configuration);
method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeState(int);
@@ -13765,7 +13765,7 @@ package android.service.wearable {
method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @BinderThread public void onDataRequestObserverUnregistered(int, @NonNull String, @NonNull android.service.wearable.WearableSensingDataRequester, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @BinderThread public abstract void onDataStreamProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @BinderThread public abstract void onQueryServiceStatus(@NonNull java.util.Set<java.lang.Integer>, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>);
- method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @BinderThread public void onSecureWearableConnectionProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @BinderThread public void onSecureConnectionProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @BinderThread public abstract void onStartDetection(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionResult>);
method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @BinderThread public void onStartHotwordRecognition(@NonNull java.util.function.Consumer<android.service.voice.HotwordAudioStream>, @NonNull java.util.function.Consumer<java.lang.Integer>);
method public abstract void onStopDetection(@NonNull String);
@@ -13946,12 +13946,24 @@ package android.telecom {
}
public final class DisconnectCause implements android.os.Parcelable {
- ctor @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public DisconnectCause(int, @NonNull CharSequence, @NonNull CharSequence, @NonNull String, int, int, int, @Nullable android.telephony.ims.ImsReasonInfo);
method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @Nullable public android.telephony.ims.ImsReasonInfo getImsReasonInfo();
method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public int getTelephonyDisconnectCause();
method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public int getTelephonyPreciseDisconnectCause();
}
+ @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final class DisconnectCause.Builder {
+ ctor public DisconnectCause.Builder();
+ method @NonNull public android.telecom.DisconnectCause build();
+ method @NonNull public android.telecom.DisconnectCause.Builder setCode(int);
+ method @NonNull public android.telecom.DisconnectCause.Builder setDescription(@Nullable CharSequence);
+ method @NonNull public android.telecom.DisconnectCause.Builder setImsReasonInfo(@Nullable android.telephony.ims.ImsReasonInfo);
+ method @NonNull public android.telecom.DisconnectCause.Builder setLabel(@Nullable CharSequence);
+ method @NonNull public android.telecom.DisconnectCause.Builder setReason(@NonNull String);
+ method @NonNull public android.telecom.DisconnectCause.Builder setTelephonyDisconnectCause(int);
+ method @NonNull public android.telecom.DisconnectCause.Builder setTelephonyPreciseDisconnectCause(int);
+ method @NonNull public android.telecom.DisconnectCause.Builder setTone(int);
+ }
+
public abstract class InCallService extends android.app.Service {
method @Deprecated public android.telecom.Phone getPhone();
method @Deprecated public void onPhoneCreated(android.telecom.Phone);
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 4cad58521c09..c58624e9dfab 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -26,6 +26,7 @@ import android.os.Build;
import android.util.LongArray;
import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicReference;
/**
* This is the superclass for classes which provide basic support for animations which can be
@@ -76,7 +77,7 @@ public abstract class Animator implements Cloneable {
* of it in case the list is modified while iterating. The array can be reused to avoid
* allocation on every notification.
*/
- private Object[] mCachedList;
+ private AtomicReference<Object[]> mCachedList = new AtomicReference<>();
/**
* Tracks whether we've notified listeners of the onAnimationStart() event. This can be
@@ -452,7 +453,7 @@ public abstract class Animator implements Cloneable {
if (mPauseListeners != null) {
anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(mPauseListeners);
}
- anim.mCachedList = null;
+ anim.mCachedList.set(null);
anim.mStartListenersCalled = false;
return anim;
} catch (CloneNotSupportedException e) {
@@ -654,13 +655,9 @@ public abstract class Animator implements Cloneable {
int size = list == null ? 0 : list.size();
if (size > 0) {
// Try to reuse mCacheList to store the items of list.
- Object[] array;
- if (mCachedList == null || mCachedList.length < size) {
+ Object[] array = mCachedList.getAndSet(null);
+ if (array == null || array.length < size) {
array = new Object[size];
- } else {
- array = mCachedList;
- // Clear it in case there is some reentrancy
- mCachedList = null;
}
list.toArray(array);
for (int i = 0; i < size; i++) {
@@ -670,7 +667,7 @@ public abstract class Animator implements Cloneable {
array[i] = null;
}
// Store it for the next call so we can reuse this array, if needed.
- mCachedList = array;
+ mCachedList.compareAndSet(null, array);
}
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 3ec39b5145a7..dd6bc55421ef 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -120,6 +120,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LauncherIcons;
import android.util.Log;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.Immutable;
@@ -4047,11 +4048,16 @@ public class ApplicationPackageManager extends PackageManager {
@Nullable
private Drawable getArchivedAppIcon(String packageName) {
try {
- return new BitmapDrawable(null,
- mPM.getArchivedAppIcon(packageName, new UserHandle(getUserId()),
- mContext.getPackageName()));
+ Bitmap archivedAppIcon = mPM.getArchivedAppIcon(packageName,
+ new UserHandle(getUserId()),
+ mContext.getPackageName());
+ if (archivedAppIcon == null) {
+ return null;
+ }
+ return new BitmapDrawable(null, archivedAppIcon);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ Slog.e(TAG, "Failed to retrieve archived app icon: " + e.getMessage());
+ return null;
}
}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index da0cc01e363d..41b97d0ad5d2 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -118,6 +118,8 @@ per-file *ScreenCapture* = file:/services/core/java/com/android/server/wm/OWNERS
# Multitasking
per-file multitasking.aconfig = file:/services/core/java/com/android/server/wm/OWNERS
per-file multitasking.aconfig = file:/libs/WindowManager/Shell/OWNERS
+per-file PictureInPicture* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file PictureInPicture* = file:/libs/WindowManager/Shell/OWNERS
# Zygote
per-file *Zygote* = file:/ZYGOTE_OWNERS
diff --git a/core/java/android/app/PictureInPictureUiState.java b/core/java/android/app/PictureInPictureUiState.java
index 39ba54cfeb0f..162953662d39 100644
--- a/core/java/android/app/PictureInPictureUiState.java
+++ b/core/java/android/app/PictureInPictureUiState.java
@@ -31,12 +31,12 @@ import java.util.Objects;
public final class PictureInPictureUiState implements Parcelable {
private final boolean mIsStashed;
- private final boolean mIsEnteringPip;
+ private final boolean mIsTransitioningToPip;
/** {@hide} */
PictureInPictureUiState(Parcel in) {
mIsStashed = in.readBoolean();
- mIsEnteringPip = in.readBoolean();
+ mIsTransitioningToPip = in.readBoolean();
}
/** {@hide} */
@@ -45,9 +45,9 @@ public final class PictureInPictureUiState implements Parcelable {
this(isStashed, false /* isEnteringPip */);
}
- private PictureInPictureUiState(boolean isStashed, boolean isEnteringPip) {
+ private PictureInPictureUiState(boolean isStashed, boolean isTransitioningToPip) {
mIsStashed = isStashed;
- mIsEnteringPip = isEnteringPip;
+ mIsTransitioningToPip = isTransitioningToPip;
}
/**
@@ -77,14 +77,14 @@ public final class PictureInPictureUiState implements Parcelable {
* whether via auto enter PiP or calling
* {@link Activity#enterPictureInPictureMode(PictureInPictureParams)} explicitly, app can expect
* {@link Activity#onPictureInPictureUiStateChanged(PictureInPictureUiState)} callback with
- * {@link #isEnteringPip()} to be {@code true} first,
+ * {@link #isTransitioningToPip()} to be {@code true} first,
* followed by {@link Activity#onPictureInPictureModeChanged(boolean, Configuration)} when it
* fully settles in PiP mode.
*
* When app receives the
* {@link Activity#onPictureInPictureUiStateChanged(PictureInPictureUiState)} callback with
- * {@link #isEnteringPip()} being {@code true}, it's recommended to hide certain UI elements,
- * such as video controls, to archive a clean entering PiP animation.
+ * {@link #isTransitioningToPip()} being {@code true}, it's recommended to hide certain UI
+ * elements, such as video controls, to archive a clean entering PiP animation.
*
* In case an application wants to restore the previously hidden UI elements when exiting
* PiP, it is recommended to do that in
@@ -92,8 +92,8 @@ public final class PictureInPictureUiState implements Parcelable {
* than the beginning of exit PiP animation.
*/
@FlaggedApi(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
- public boolean isEnteringPip() {
- return mIsEnteringPip;
+ public boolean isTransitioningToPip() {
+ return mIsTransitioningToPip;
}
@Override
@@ -102,12 +102,12 @@ public final class PictureInPictureUiState implements Parcelable {
if (!(o instanceof PictureInPictureUiState)) return false;
PictureInPictureUiState that = (PictureInPictureUiState) o;
return mIsStashed == that.mIsStashed
- && mIsEnteringPip == that.mIsEnteringPip;
+ && mIsTransitioningToPip == that.mIsTransitioningToPip;
}
@Override
public int hashCode() {
- return Objects.hash(mIsStashed, mIsEnteringPip);
+ return Objects.hash(mIsStashed, mIsTransitioningToPip);
}
@Override
@@ -118,7 +118,7 @@ public final class PictureInPictureUiState implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeBoolean(mIsStashed);
- out.writeBoolean(mIsEnteringPip);
+ out.writeBoolean(mIsTransitioningToPip);
}
public static final @android.annotation.NonNull Creator<PictureInPictureUiState> CREATOR =
@@ -138,7 +138,7 @@ public final class PictureInPictureUiState implements Parcelable {
@FlaggedApi(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
public static final class Builder {
private boolean mIsStashed;
- private boolean mIsEnteringPip;
+ private boolean mIsTransitioningToPip;
/** Empty constructor. */
public Builder() {
@@ -154,11 +154,11 @@ public final class PictureInPictureUiState implements Parcelable {
}
/**
- * Sets the {@link #mIsEnteringPip} state.
+ * Sets the {@link #mIsTransitioningToPip} state.
* @return The same {@link Builder} instance.
*/
- public Builder setEnteringPip(boolean isEnteringPip) {
- mIsEnteringPip = isEnteringPip;
+ public Builder setTransitioningToPip(boolean isEnteringPip) {
+ mIsTransitioningToPip = isEnteringPip;
return this;
}
@@ -166,7 +166,7 @@ public final class PictureInPictureUiState implements Parcelable {
* @return The constructed {@link PictureInPictureUiState} instance.
*/
public PictureInPictureUiState build() {
- return new PictureInPictureUiState(mIsStashed, mIsEnteringPip);
+ return new PictureInPictureUiState(mIsStashed, mIsTransitioningToPip);
}
}
}
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 9fa73627de05..fb1b17bd23d4 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -10,6 +10,7 @@ import android.annotation.SystemApi;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
+import android.credentials.CredentialOption;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
import android.credentials.GetCredentialResponse;
@@ -29,6 +30,7 @@ import android.os.PooledStringWriter;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.FillRequest;
+import android.service.credentials.CredentialProviderService;
import android.text.InputType;
import android.text.Spanned;
import android.text.TextUtils;
@@ -913,6 +915,7 @@ public class AssistStructure implements Parcelable {
if ((flags&FLAGS_HAS_EXTRAS) != 0) {
mExtras = in.readBundle();
}
+ mGetCredentialRequest = in.readTypedObject(GetCredentialRequest.CREATOR);
}
/**
@@ -1149,6 +1152,7 @@ public class AssistStructure implements Parcelable {
if ((flags&FLAGS_HAS_EXTRAS) != 0) {
out.writeBundle(mExtras);
}
+ out.writeTypedObject(mGetCredentialRequest, flags);
return flags;
}
@@ -1287,11 +1291,7 @@ public class AssistStructure implements Parcelable {
}
/**
- *
- * @return
- *
* @hide
- *
*/
@FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
@Nullable
@@ -2260,6 +2260,17 @@ public class AssistStructure implements Parcelable {
@NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
mNode.mGetCredentialRequest = request;
mNode.mGetCredentialCallback = callback;
+ for (CredentialOption option : request.getCredentialOptions()) {
+ ArrayList<AutofillId> ids = option.getCandidateQueryData()
+ .getParcelableArrayList(
+ CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class);
+ ids = ids != null ? ids : new ArrayList<>();
+ if (!ids.contains(getAutofillId())) {
+ ids.add(getAutofillId());
+ }
+ option.getCandidateQueryData()
+ .putParcelableArrayList(CredentialProviderService.EXTRA_AUTOFILL_ID, ids);
+ }
}
@Override
@@ -2569,7 +2580,7 @@ public class AssistStructure implements Parcelable {
}
AutofillId autofillId = node.getAutofillId();
if (autofillId == null) {
- Log.i(TAG, prefix + " NO autofill ID");
+ Log.i(TAG, prefix + " No autofill ID");
} else {
Log.i(TAG, prefix + " Autofill info: id= " + autofillId
+ ", type=" + node.getAutofillType()
@@ -2584,7 +2595,7 @@ public class AssistStructure implements Parcelable {
}
GetCredentialRequest getCredentialRequest = node.getCredentialManagerRequest();
if (getCredentialRequest == null) {
- Log.i(TAG, prefix + " NO Credential Manager Request");
+ Log.i(TAG, prefix + " No Credential Manager Request");
} else {
Log.i(TAG, prefix + " GetCredentialRequest: no. of options= "
+ getCredentialRequest.getCredentialOptions().size()
diff --git a/core/java/android/app/wearable/IWearableSensingManager.aidl b/core/java/android/app/wearable/IWearableSensingManager.aidl
index f67802279e26..7d3b28511537 100644
--- a/core/java/android/app/wearable/IWearableSensingManager.aidl
+++ b/core/java/android/app/wearable/IWearableSensingManager.aidl
@@ -30,7 +30,7 @@ import android.os.SharedMemory;
*/
interface IWearableSensingManager {
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
- void provideWearableConnection(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
+ void provideConnection(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java
index 637f6776bd1b..fd72c491bf16 100644
--- a/core/java/android/app/wearable/WearableSensingManager.java
+++ b/core/java/android/app/wearable/WearableSensingManager.java
@@ -127,7 +127,7 @@ public class WearableSensingManager {
/**
* The value of the status code that indicates an error occurred in the encrypted channel backed
- * by the provided connection. See {@link #provideWearableConnection(ParcelFileDescriptor,
+ * by the provided connection. See {@link #provideConnection(ParcelFileDescriptor,
* Executor, Consumer)}.
*/
@FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
@@ -223,13 +223,13 @@ public class WearableSensingManager {
*/
@RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
@FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
- public void provideWearableConnection(
+ public void provideConnection(
@NonNull ParcelFileDescriptor wearableConnection,
@NonNull @CallbackExecutor Executor executor,
@NonNull @StatusCode Consumer<Integer> statusConsumer) {
try {
RemoteCallback callback = createStatusCallback(executor, statusConsumer);
- mService.provideWearableConnection(wearableConnection, callback);
+ mService.provideConnection(wearableConnection, callback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 13c3ededbcd7..7505372fe295 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -6559,6 +6559,13 @@ public abstract class Context {
public static final String ECM_ENHANCED_CONFIRMATION_SERVICE = "ecm_enhanced_confirmation";
/**
+ * Service to protect sensitive content during screen share.
+ * @hide
+ */
+ public static final String SENSITIVE_CONTENT_PROTECTION_SERVICE =
+ "sensitive_content_protection_service";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.provider.ContactKeysManager} to managing contact keys.
*
diff --git a/core/java/android/content/pm/SignedPackage.java b/core/java/android/content/pm/SignedPackage.java
index 4d1b136915f2..7bffa7722142 100644
--- a/core/java/android/content/pm/SignedPackage.java
+++ b/core/java/android/content/pm/SignedPackage.java
@@ -35,9 +35,9 @@ public class SignedPackage {
private final SignedPackageParcel mData;
/** @hide */
- public SignedPackage(@NonNull String pkgName, @NonNull byte[] certificateDigest) {
+ public SignedPackage(@NonNull String packageName, @NonNull byte[] certificateDigest) {
SignedPackageParcel data = new SignedPackageParcel();
- data.pkgName = pkgName;
+ data.packageName = packageName;
data.certificateDigest = certificateDigest;
mData = data;
}
@@ -52,8 +52,8 @@ public class SignedPackage {
return mData;
}
- public @NonNull String getPkgName() {
- return mData.pkgName;
+ public @NonNull String getPackageName() {
+ return mData.packageName;
}
public @NonNull byte[] getCertificateDigest() {
@@ -64,12 +64,12 @@ public class SignedPackage {
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SignedPackage that)) return false;
- return mData.pkgName.equals(that.mData.pkgName) && Arrays.equals(mData.certificateDigest,
- that.mData.certificateDigest);
+ return mData.packageName.equals(that.mData.packageName) && Arrays.equals(
+ mData.certificateDigest, that.mData.certificateDigest);
}
@Override
public int hashCode() {
- return Objects.hash(mData.pkgName, Arrays.hashCode(mData.certificateDigest));
+ return Objects.hash(mData.packageName, Arrays.hashCode(mData.certificateDigest));
}
}
diff --git a/core/java/android/content/pm/SignedPackageParcel.aidl b/core/java/android/content/pm/SignedPackageParcel.aidl
index 7957f7f99d4a..bb4dc8654108 100644
--- a/core/java/android/content/pm/SignedPackageParcel.aidl
+++ b/core/java/android/content/pm/SignedPackageParcel.aidl
@@ -20,6 +20,6 @@ import android.content.ComponentName;
/** @hide */
parcelable SignedPackageParcel {
- String pkgName;
+ String packageName;
byte[] certificateDigest;
}
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index d347a0e8ae63..915992904a5c 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -537,7 +537,6 @@ public final class UserProperties implements Parcelable {
setDeleteAppWithParent(orig.getDeleteAppWithParent());
setAlwaysVisible(orig.getAlwaysVisible());
setAllowStoppingUserWithDelayedLocking(orig.getAllowStoppingUserWithDelayedLocking());
- setItemsRestrictedOnHomeScreen(orig.areItemsRestrictedOnHomeScreen());
}
if (hasManagePermission) {
// Add items that require MANAGE_USERS or stronger.
@@ -557,6 +556,7 @@ public final class UserProperties implements Parcelable {
setShowInSharingSurfaces(orig.getShowInSharingSurfaces());
setCrossProfileContentSharingStrategy(orig.getCrossProfileContentSharingStrategy());
setProfileApiVisibility(orig.getProfileApiVisibility());
+ setItemsRestrictedOnHomeScreen(orig.areItemsRestrictedOnHomeScreen());
}
/**
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 56849291e52a..22e233aebf36 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -144,6 +144,13 @@ flag {
}
flag {
+ name: "show_set_screen_lock_dialog"
+ namespace: "profile_experiences"
+ description: "Display the dialog to set up screen lock when private space unlock operation is requested"
+ bug: "316129700"
+}
+
+flag {
name: "reorder_wallpaper_during_user_switch"
namespace: "multiuser"
description: "Reorder loading home and lock screen wallpapers during a user switch."
@@ -163,3 +170,10 @@ flag {
description: "Disables adding items belonging to Private Space on Home Screen manually as well as automatically"
bug: "287975131"
}
+
+flag {
+ name: "enable_ps_sensitive_notifications_toggle"
+ namespace: "profile_experiences"
+ description: "Enable the sensitive notifications toggle to be visible in the Private space settings page"
+ bug: "317067050"
+}
diff --git a/core/java/android/credentials/GetCredentialResponse.java b/core/java/android/credentials/GetCredentialResponse.java
index ea699b9a74e5..a1477ee1a43a 100644
--- a/core/java/android/credentials/GetCredentialResponse.java
+++ b/core/java/android/credentials/GetCredentialResponse.java
@@ -63,9 +63,6 @@ public final class GetCredentialResponse implements Parcelable {
}
/**
- *
- * @return
- *
* @hide
*/
public AutofillId getAutofillId() {
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index ef7b2594fd5d..09e59d315428 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -68,4 +68,18 @@ flag {
name: "new_framework_metrics"
description: "Enables new metrics fror 24Q3 / VIC"
bug: "324291187"
-} \ No newline at end of file
+}
+
+flag {
+ namespace: "credential_manager"
+ name: "clear_credentials_api_fix_enabled"
+ description: "Fixes bug in clearCredential API that causes indefinite suspension"
+ bug: "314926460"
+}
+
+flag {
+ namespace: "credential_manager"
+ name: "hybrid_filter_fix_enabled"
+ description: "Removes capability check from hybrid implementation"
+ bug: "323923403"
+}
diff --git a/core/java/android/credentials/selection/Constants.java b/core/java/android/credentials/selection/Constants.java
index f7fec237dd08..2229f258a3a4 100644
--- a/core/java/android/credentials/selection/Constants.java
+++ b/core/java/android/credentials/selection/Constants.java
@@ -30,13 +30,6 @@ public class Constants {
"android.credentials.selection.extra.RESULT_RECEIVER";
/**
- * The intent extra key for indicating whether the bottom sheet should be started directly
- * on the 'All Options' screen.
- */
- public static final String EXTRA_REQ_FOR_ALL_OPTIONS =
- "android.credentials.selection.extra.REQ_FOR_ALL_OPTIONS";
-
- /**
* The intent extra key for the final result receiver object
*/
public static final String EXTRA_FINAL_RESPONSE_RECEIVER =
diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java
index ac2bae495219..4b0fa6d32af7 100644
--- a/core/java/android/credentials/selection/IntentFactory.java
+++ b/core/java/android/credentials/selection/IntentFactory.java
@@ -48,41 +48,28 @@ import java.util.ArrayList;
public class IntentFactory {
/**
- * Generate a new launch intent to the Credential Selector UI for auto-filling.
+ * Generate a new launch intent to the Credential Selector UI for auto-filling. This intent is
+ * invoked from the Autofill flow, when the user requests to bring up the 'All Options' page of
+ * the credential bottom-sheet. When the user clicks on the pinned entry, the intent will bring
+ * up the 'All Options' page of the bottom-sheet. The provider data list is processed by the
+ * credential autofill service for each autofill id and passed in as an auth extra.
*
* @hide
*/
@NonNull
- // TODO(b/323552850) - clean up method overloads
- public static Intent createCredentialSelectorIntent(
+ public static Intent createCredentialSelectorIntentForAutofill(
@NonNull Context context,
@NonNull RequestInfo requestInfo,
@SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
- @Nullable
- ArrayList<ProviderData> enabledProviderDataList,
- @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
@NonNull
ArrayList<DisabledProviderData> disabledProviderDataList,
- @NonNull ResultReceiver resultReceiver,
- boolean isRequestForAllOptions) {
-
- Intent intent;
- if (enabledProviderDataList != null) {
- intent = createCredentialSelectorIntent(context, requestInfo, enabledProviderDataList,
- disabledProviderDataList, resultReceiver);
- } else {
- intent = createCredentialSelectorIntent(context, requestInfo,
- disabledProviderDataList, resultReceiver);
- }
- intent.putExtra(Constants.EXTRA_REQ_FOR_ALL_OPTIONS, isRequestForAllOptions);
-
- return intent;
+ @NonNull ResultReceiver resultReceiver) {
+ return createCredentialSelectorIntent(context, requestInfo,
+ disabledProviderDataList, resultReceiver);
}
/**
* Generate a new launch intent to the Credential Selector UI.
- *
- * @hide
*/
@NonNull
private static Intent createCredentialSelectorIntent(
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index 2b62b98529a9..2ba1d897fe34 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -19,6 +19,8 @@ package android.hardware.biometrics;
import android.annotation.IntDef;
import android.app.KeyguardManager;
import android.hardware.biometrics.BiometricManager.Authenticators;
+import android.hardware.face.FaceEnrollOptions;
+import android.hardware.face.FaceEnrollOptions.EnrollReason;
import android.hardware.face.FaceManager;
import java.lang.annotation.Retention;
@@ -432,4 +434,22 @@ public interface BiometricFaceConstants {
* vendor code.
*/
int FACE_ACQUIRED_VENDOR_BASE = 1000;
+
+
+ /**
+ * Converts FaceEnrollOptions.reason into BiometricsProtoEnums.enrollReason
+ */
+ public static int reasonToMetric(@EnrollReason int reason) {
+ switch (reason) {
+ case FaceEnrollOptions.ENROLL_REASON_RE_ENROLL_NOTIFICATION:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_FRR_NOTIFICATION;
+ case FaceEnrollOptions.ENROLL_REASON_SETTINGS:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_SETTINGS;
+ case FaceEnrollOptions.ENROLL_REASON_SUW:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW;
+ default:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_UNKNOWN;
+ }
+
+ }
}
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index 5b24fb6860a2..770448bd594b 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -20,6 +20,8 @@ import android.annotation.IntDef;
import android.app.KeyguardManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.biometrics.BiometricManager.Authenticators;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
+import android.hardware.fingerprint.FingerprintEnrollOptions.EnrollReason;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
@@ -350,4 +352,22 @@ public interface BiometricFingerprintConstants {
return false;
}
}
+
+
+ /**
+ * Converts FaceEnrollOptions.reason into BiometricsProtoEnums.enrollReason
+ */
+ static int reasonToMetric(@EnrollReason int reason) {
+ switch(reason) {
+ case FingerprintEnrollOptions.ENROLL_REASON_RE_ENROLL_NOTIFICATION:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_FRR_NOTIFICATION;
+ case FingerprintEnrollOptions.ENROLL_REASON_SETTINGS:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_SETTINGS;
+ case FingerprintEnrollOptions.ENROLL_REASON_SUW:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW;
+ default:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_UNKNOWN;
+ }
+
+ }
}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index d7d1d1a7c677..fdd8b04921f5 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -100,6 +100,13 @@ public class BiometricManager {
Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_STRONG;
/**
+ * Enroll reason extra that can be used by settings to understand where this request came
+ * from.
+ * @hide
+ */
+ public static final String EXTRA_ENROLL_REASON = "enroll_reason";
+
+ /**
* @hide
*/
@IntDef({BIOMETRIC_SUCCESS,
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 76c20ce2184a..749f218b0e6a 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -874,8 +874,11 @@ public final class CameraExtensionCharacteristics {
Class<CameraCharacteristics.Key<?>> keyTyped =
(Class<CameraCharacteristics.Key<?>>) key;
+ // Do not include synthetic keys. Including synthetic keys leads to undefined
+ // behavior. This causes inclusion of capabilities that may not be supported in
+ // camera extensions.
ret.addAll(chars.getAvailableKeyList(CameraCharacteristics.class, keyTyped, keys,
- /*includeSynthetic*/ true));
+ /*includeSynthetic*/ false));
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension for all available keys! Extension "
diff --git a/core/java/android/hardware/face/FaceEnrollOptions.aidl b/core/java/android/hardware/face/FaceEnrollOptions.aidl
new file mode 100644
index 000000000000..336de9db899c
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollOptions.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.face;
+
+parcelable FaceEnrollOptions;
diff --git a/core/java/android/hardware/face/FaceEnrollOptions.java b/core/java/android/hardware/face/FaceEnrollOptions.java
new file mode 100644
index 000000000000..440105584f74
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollOptions.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.face;
+
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Additional options when requesting Face enrollment.
+ *
+ * @hide
+ */
+@DataClass(
+ genParcelable = true,
+ genAidl = true,
+ genBuilder = true,
+ genSetters = true,
+ genEqualsHashCode = true
+)
+public class FaceEnrollOptions implements Parcelable {
+ public static final int ENROLL_REASON_UNKNOWN = 0;
+ public static final int ENROLL_REASON_RE_ENROLL_NOTIFICATION = 1;
+ public static final int ENROLL_REASON_SETTINGS = 2;
+ public static final int ENROLL_REASON_SUW = 3;
+
+ /**
+ * The reason for enrollment.
+ */
+ @EnrollReason
+ private final int mEnrollReason;
+ private static int defaultEnrollReason() {
+ return ENROLL_REASON_UNKNOWN;
+ }
+
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/face/FaceEnrollOptions.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @android.annotation.IntDef(prefix = "ENROLL_REASON_", value = {
+ ENROLL_REASON_UNKNOWN,
+ ENROLL_REASON_RE_ENROLL_NOTIFICATION,
+ ENROLL_REASON_SETTINGS,
+ ENROLL_REASON_SUW
+ })
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface EnrollReason {}
+
+ @DataClass.Generated.Member
+ public static String enrollReasonToString(@EnrollReason int value) {
+ switch (value) {
+ case ENROLL_REASON_UNKNOWN:
+ return "ENROLL_REASON_UNKNOWN";
+ case ENROLL_REASON_RE_ENROLL_NOTIFICATION:
+ return "ENROLL_REASON_RE_ENROLL_NOTIFICATION";
+ case ENROLL_REASON_SETTINGS:
+ return "ENROLL_REASON_SETTINGS";
+ case ENROLL_REASON_SUW:
+ return "ENROLL_REASON_SUW";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ @DataClass.Generated.Member
+ /* package-private */ FaceEnrollOptions(
+ @EnrollReason int enrollReason) {
+ this.mEnrollReason = enrollReason;
+
+ if (!(mEnrollReason == ENROLL_REASON_UNKNOWN)
+ && !(mEnrollReason == ENROLL_REASON_RE_ENROLL_NOTIFICATION)
+ && !(mEnrollReason == ENROLL_REASON_SETTINGS)
+ && !(mEnrollReason == ENROLL_REASON_SUW)) {
+ throw new java.lang.IllegalArgumentException(
+ "enrollReason was " + mEnrollReason + " but must be one of: "
+ + "ENROLL_REASON_UNKNOWN(" + ENROLL_REASON_UNKNOWN + "), "
+ + "ENROLL_REASON_RE_ENROLL_NOTIFICATION(" + ENROLL_REASON_RE_ENROLL_NOTIFICATION + "), "
+ + "ENROLL_REASON_SETTINGS(" + ENROLL_REASON_SETTINGS + "), "
+ + "ENROLL_REASON_SUW(" + ENROLL_REASON_SUW + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The reason for enrollment.
+ */
+ @DataClass.Generated.Member
+ public @EnrollReason int getEnrollReason() {
+ return mEnrollReason;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(FaceEnrollOptions other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ FaceEnrollOptions that = (FaceEnrollOptions) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mEnrollReason == that.mEnrollReason;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mEnrollReason;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mEnrollReason);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected FaceEnrollOptions(@android.annotation.NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int enrollReason = in.readInt();
+
+ this.mEnrollReason = enrollReason;
+
+ if (!(mEnrollReason == ENROLL_REASON_UNKNOWN)
+ && !(mEnrollReason == ENROLL_REASON_RE_ENROLL_NOTIFICATION)
+ && !(mEnrollReason == ENROLL_REASON_SETTINGS)
+ && !(mEnrollReason == ENROLL_REASON_SUW)) {
+ throw new java.lang.IllegalArgumentException(
+ "enrollReason was " + mEnrollReason + " but must be one of: "
+ + "ENROLL_REASON_UNKNOWN(" + ENROLL_REASON_UNKNOWN + "), "
+ + "ENROLL_REASON_RE_ENROLL_NOTIFICATION(" + ENROLL_REASON_RE_ENROLL_NOTIFICATION + "), "
+ + "ENROLL_REASON_SETTINGS(" + ENROLL_REASON_SETTINGS + "), "
+ + "ENROLL_REASON_SUW(" + ENROLL_REASON_SUW + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @android.annotation.NonNull Parcelable.Creator<FaceEnrollOptions> CREATOR
+ = new Parcelable.Creator<FaceEnrollOptions>() {
+ @Override
+ public FaceEnrollOptions[] newArray(int size) {
+ return new FaceEnrollOptions[size];
+ }
+
+ @Override
+ public FaceEnrollOptions createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+ return new FaceEnrollOptions(in);
+ }
+ };
+
+ /**
+ * A builder for {@link FaceEnrollOptions}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static class Builder {
+
+ private @EnrollReason int mEnrollReason;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * The reason for enrollment.
+ */
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull Builder setEnrollReason(@EnrollReason int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mEnrollReason = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @android.annotation.NonNull FaceEnrollOptions build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mEnrollReason = defaultEnrollReason();
+ }
+ FaceEnrollOptions o = new FaceEnrollOptions(
+ mEnrollReason);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x2) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1707949032303L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/hardware/face/FaceEnrollOptions.java",
+ inputSignatures = "public static final int ENROLL_REASON_UNKNOWN\npublic static final int ENROLL_REASON_RE_ENROLL_NOTIFICATION\npublic static final int ENROLL_REASON_SETTINGS\npublic static final int ENROLL_REASON_SUW\nprivate final @android.hardware.face.FaceEnrollOptions.EnrollReason int mEnrollReason\nprivate static int defaultEnrollReason()\nclass FaceEnrollOptions extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index bae5e7f83569..066c45f03ec4 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -393,7 +393,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
EnrollmentCallback callback, int[] disabledFeatures) {
enroll(userId, hardwareAuthToken, cancel, callback, disabledFeatures,
- null /* previewSurface */, false /* debugConsent */);
+ null /* previewSurface */, false /* debugConsent */,
+ (new FaceEnrollOptions.Builder()).build());
+
}
/**
@@ -418,7 +420,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
@RequiresPermission(MANAGE_BIOMETRIC)
public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
EnrollmentCallback callback, int[] disabledFeatures, @Nullable Surface previewSurface,
- boolean debugConsent) {
+ boolean debugConsent, FaceEnrollOptions options) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
}
@@ -449,7 +451,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
Trace.beginSection("FaceManager#enroll");
final long enrollId = mService.enroll(userId, mToken, hardwareAuthToken,
mServiceReceiver, mContext.getOpPackageName(), disabledFeatures,
- previewSurface, debugConsent);
+ previewSurface, debugConsent, options);
if (cancel != null) {
cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 8e234fa11866..b98c0cb41ac9 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -26,6 +26,7 @@ import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.FaceSensorConfigurations;
import android.view.Surface;
@@ -100,7 +101,7 @@ interface IFaceService {
@EnforcePermission("MANAGE_BIOMETRIC")
long enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
String opPackageName, in int [] disabledFeatures,
- in Surface previewSurface, boolean debugConsent);
+ in Surface previewSurface, boolean debugConsent, in FaceEnrollOptions options);
// Start remote face enrollment
@EnforcePermission("MANAGE_BIOMETRIC")
diff --git a/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.aidl b/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.aidl
new file mode 100644
index 000000000000..77803f3e3a9b
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.fingerprint;
+
+parcelable FingerprintEnrollOptions; \ No newline at end of file
diff --git a/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.java b/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.java
new file mode 100644
index 000000000000..9f9f322a8876
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.fingerprint;
+
+import android.os.Parcelable;
+
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Additional options when requesting Fingerprint enrollment.
+ *
+ * @hide
+ */
+@DataClass(
+ genParcelable = true,
+ genAidl = true,
+ genBuilder = true,
+ genSetters = true,
+ genEqualsHashCode = true
+)
+public class FingerprintEnrollOptions implements Parcelable {
+ public static final int ENROLL_REASON_UNKNOWN = 0;
+ public static final int ENROLL_REASON_RE_ENROLL_NOTIFICATION = 1;
+ public static final int ENROLL_REASON_SETTINGS = 2;
+ public static final int ENROLL_REASON_SUW = 3;
+
+ /**
+ * The reason for enrollment.
+ */
+ @EnrollReason
+ private final int mEnrollReason;
+
+ private static int defaultEnrollReason() {
+ return ENROLL_REASON_UNKNOWN;
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @android.annotation.IntDef(prefix = "ENROLL_REASON_", value = {
+ ENROLL_REASON_UNKNOWN,
+ ENROLL_REASON_RE_ENROLL_NOTIFICATION,
+ ENROLL_REASON_SETTINGS,
+ ENROLL_REASON_SUW
+ })
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface EnrollReason {}
+
+ @DataClass.Generated.Member
+ public static String enrollReasonToString(@EnrollReason int value) {
+ switch (value) {
+ case ENROLL_REASON_UNKNOWN:
+ return "ENROLL_REASON_UNKNOWN";
+ case ENROLL_REASON_RE_ENROLL_NOTIFICATION:
+ return "ENROLL_REASON_RE_ENROLL_NOTIFICATION";
+ case ENROLL_REASON_SETTINGS:
+ return "ENROLL_REASON_SETTINGS";
+ case ENROLL_REASON_SUW:
+ return "ENROLL_REASON_SUW";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ @DataClass.Generated.Member
+ /* package-private */ FingerprintEnrollOptions(
+ @EnrollReason int enrollReason) {
+ this.mEnrollReason = enrollReason;
+
+ if (!(mEnrollReason == ENROLL_REASON_UNKNOWN)
+ && !(mEnrollReason == ENROLL_REASON_RE_ENROLL_NOTIFICATION)
+ && !(mEnrollReason == ENROLL_REASON_SETTINGS)
+ && !(mEnrollReason == ENROLL_REASON_SUW)) {
+ throw new java.lang.IllegalArgumentException(
+ "enrollReason was " + mEnrollReason + " but must be one of: "
+ + "ENROLL_REASON_UNKNOWN(" + ENROLL_REASON_UNKNOWN + "), "
+ + "ENROLL_REASON_RE_ENROLL_NOTIFICATION(" + ENROLL_REASON_RE_ENROLL_NOTIFICATION + "), "
+ + "ENROLL_REASON_SETTINGS(" + ENROLL_REASON_SETTINGS + "), "
+ + "ENROLL_REASON_SUW(" + ENROLL_REASON_SUW + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The reason for enrollment.
+ */
+ @DataClass.Generated.Member
+ public @EnrollReason int getEnrollReason() {
+ return mEnrollReason;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(FingerprintEnrollOptions other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ FingerprintEnrollOptions that = (FingerprintEnrollOptions) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mEnrollReason == that.mEnrollReason;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mEnrollReason;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mEnrollReason);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected FingerprintEnrollOptions(@android.annotation.NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int enrollReason = in.readInt();
+
+ this.mEnrollReason = enrollReason;
+
+ if (!(mEnrollReason == ENROLL_REASON_UNKNOWN)
+ && !(mEnrollReason == ENROLL_REASON_RE_ENROLL_NOTIFICATION)
+ && !(mEnrollReason == ENROLL_REASON_SETTINGS)
+ && !(mEnrollReason == ENROLL_REASON_SUW)) {
+ throw new java.lang.IllegalArgumentException(
+ "enrollReason was " + mEnrollReason + " but must be one of: "
+ + "ENROLL_REASON_UNKNOWN(" + ENROLL_REASON_UNKNOWN + "), "
+ + "ENROLL_REASON_RE_ENROLL_NOTIFICATION(" + ENROLL_REASON_RE_ENROLL_NOTIFICATION + "), "
+ + "ENROLL_REASON_SETTINGS(" + ENROLL_REASON_SETTINGS + "), "
+ + "ENROLL_REASON_SUW(" + ENROLL_REASON_SUW + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @android.annotation.NonNull Parcelable.Creator<FingerprintEnrollOptions> CREATOR
+ = new Parcelable.Creator<FingerprintEnrollOptions>() {
+ @Override
+ public FingerprintEnrollOptions[] newArray(int size) {
+ return new FingerprintEnrollOptions[size];
+ }
+
+ @Override
+ public FingerprintEnrollOptions createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+ return new FingerprintEnrollOptions(in);
+ }
+ };
+
+ /**
+ * A builder for {@link FingerprintEnrollOptions}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static class Builder {
+
+ private @EnrollReason int mEnrollReason;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * The reason for enrollment.
+ */
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull Builder setEnrollReason(@EnrollReason int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mEnrollReason = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @android.annotation.NonNull FingerprintEnrollOptions build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mEnrollReason = defaultEnrollReason();
+ }
+ FingerprintEnrollOptions o = new FingerprintEnrollOptions(
+ mEnrollReason);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x2) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1704407629121L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.java",
+ inputSignatures = "public static final int ENROLL_REASON_UNKNOWN\npublic static final int ENROLL_REASON_RE_ENROLL_NOTIFICATION\npublic static final int ENROLL_REASON_SETTINGS\npublic static final int ENROLL_REASON_SUW\nprivate final @android.hardware.fingerprint.FingerprintEnrollOptions.EnrollReason int mEnrollReason\nprivate static int defaultEnrollReason()\nclass FingerprintEnrollOptions extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 5a38a34fde11..b0f69f56cba7 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -755,7 +755,8 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId,
- EnrollmentCallback callback, @EnrollReason int enrollReason) {
+ EnrollmentCallback callback, @EnrollReason int enrollReason,
+ FingerprintEnrollOptions options) {
if (userId == UserHandle.USER_CURRENT) {
userId = getCurrentUserId();
}
@@ -779,7 +780,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
try {
mEnrollmentCallback = callback;
final long enrollId = mService.enroll(mToken, hardwareAuthToken, userId,
- mServiceReceiver, mContext.getOpPackageName(), enrollReason);
+ mServiceReceiver, mContext.getOpPackageName(), enrollReason, options);
if (cancel != null) {
cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 606b171f36ba..8b37c24527d7 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -30,6 +30,7 @@ import android.hardware.fingerprint.IUdfpsOverlayController;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintSensorConfigurations;
import java.util.List;
@@ -98,7 +99,7 @@ interface IFingerprintService {
// Start fingerprint enrollment
@EnforcePermission("MANAGE_FINGERPRINT")
long enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
- String opPackageName, int enrollReason);
+ String opPackageName, int enrollReason, in FingerprintEnrollOptions options);
// Cancel enrollment in progress
@EnforcePermission("MANAGE_FINGERPRINT")
diff --git a/core/java/android/net/flags.aconfig b/core/java/android/net/flags.aconfig
index 311dc09eb516..6efb87225183 100644
--- a/core/java/android/net/flags.aconfig
+++ b/core/java/android/net/flags.aconfig
@@ -9,3 +9,11 @@ flag {
description: "The flag controls the access for getIpSecTransformState and IpSecTransformState"
bug: "308011229"
}
+
+flag {
+ name: "powered_off_finding_platform"
+ namespace: "nearby"
+ description: "Controls whether the Powered Off Finding feature is enabled"
+ bug: "307898240"
+}
+
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index c9c91fc49aeb..efbd96bc35cb 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -591,9 +591,14 @@ public abstract class VibrationEffect implements Parcelable {
/**
* Scale given vibration intensity by the given factor.
*
+ * <p> This scale is not necessarily linear and may apply a gamma correction to the scale
+ * factor before using it.
+ *
* @param intensity relative intensity of the effect, must be between 0 and 1
* @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
* scale down the intensity, values larger than 1 will scale up
+ * @return the scaled intensity which will be values within [0, 1].
+ *
* @hide
*/
public static float scale(float intensity, float scaleFactor) {
@@ -624,6 +629,20 @@ public abstract class VibrationEffect implements Parcelable {
}
/**
+ * Performs a linear scaling on the given vibration intensity by the given factor.
+ *
+ * @param intensity relative intensity of the effect, must be between 0 and 1.
+ * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
+ * scale down the intensity, values larger than 1 will scale up.
+ * @return the scaled intensity which will be values within [0, 1].
+ *
+ * @hide
+ */
+ public static float scaleLinearly(float intensity, float scaleFactor) {
+ return MathUtils.constrain(intensity * scaleFactor, 0f, 1f);
+ }
+
+ /**
* Returns a compact version of the {@link #toString()} result for debugging purposes.
*
* @hide
diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java
index a035092e314f..39f841226e4e 100644
--- a/core/java/android/os/vibrator/PrebakedSegment.java
+++ b/core/java/android/os/vibrator/PrebakedSegment.java
@@ -137,6 +137,14 @@ public final class PrebakedSegment extends VibrationEffectSegment {
/** @hide */
@NonNull
@Override
+ public PrebakedSegment scaleLinearly(float scaleFactor) {
+ // Prebaked effect strength cannot be scaled with this method.
+ return this;
+ }
+
+ /** @hide */
+ @NonNull
+ @Override
public PrebakedSegment applyEffectStrength(int effectStrength) {
if (effectStrength != mEffectStrength && isValidEffectStrength(effectStrength)) {
return new PrebakedSegment(mEffectId, mFallback, effectStrength);
diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java
index 95d97bfe4ad1..3c84bcda639b 100644
--- a/core/java/android/os/vibrator/PrimitiveSegment.java
+++ b/core/java/android/os/vibrator/PrimitiveSegment.java
@@ -98,8 +98,24 @@ public final class PrimitiveSegment extends VibrationEffectSegment {
@NonNull
@Override
public PrimitiveSegment scale(float scaleFactor) {
- return new PrimitiveSegment(mPrimitiveId, VibrationEffect.scale(mScale, scaleFactor),
- mDelay);
+ float newScale = VibrationEffect.scale(mScale, scaleFactor);
+ if (Float.compare(mScale, newScale) == 0) {
+ return this;
+ }
+
+ return new PrimitiveSegment(mPrimitiveId, newScale, mDelay);
+ }
+
+ /** @hide */
+ @NonNull
+ @Override
+ public PrimitiveSegment scaleLinearly(float scaleFactor) {
+ float newScale = VibrationEffect.scaleLinearly(mScale, scaleFactor);
+ if (Float.compare(mScale, newScale) == 0) {
+ return this;
+ }
+
+ return new PrimitiveSegment(mPrimitiveId, newScale, mDelay);
}
/** @hide */
diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java
index 5f9d1024d9a5..09d2e26be2a5 100644
--- a/core/java/android/os/vibrator/RampSegment.java
+++ b/core/java/android/os/vibrator/RampSegment.java
@@ -159,6 +159,21 @@ public final class RampSegment extends VibrationEffectSegment {
/** @hide */
@NonNull
@Override
+ public RampSegment scaleLinearly(float scaleFactor) {
+ float newStartAmplitude = VibrationEffect.scaleLinearly(mStartAmplitude, scaleFactor);
+ float newEndAmplitude = VibrationEffect.scaleLinearly(mEndAmplitude, scaleFactor);
+ if (Float.compare(mStartAmplitude, newStartAmplitude) == 0
+ && Float.compare(mEndAmplitude, newEndAmplitude) == 0) {
+ return this;
+ }
+ return new RampSegment(newStartAmplitude, newEndAmplitude, mStartFrequencyHz,
+ mEndFrequencyHz,
+ mDuration);
+ }
+
+ /** @hide */
+ @NonNull
+ @Override
public RampSegment applyEffectStrength(int effectStrength) {
return this;
}
diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java
index 9576a5bba1f1..fa083c176a27 100644
--- a/core/java/android/os/vibrator/StepSegment.java
+++ b/core/java/android/os/vibrator/StepSegment.java
@@ -137,8 +137,25 @@ public final class StepSegment extends VibrationEffectSegment {
if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0) {
return this;
}
- return new StepSegment(VibrationEffect.scale(mAmplitude, scaleFactor), mFrequencyHz,
- mDuration);
+ float newAmplitude = VibrationEffect.scale(mAmplitude, scaleFactor);
+ if (Float.compare(newAmplitude, mAmplitude) == 0) {
+ return this;
+ }
+ return new StepSegment(newAmplitude, mFrequencyHz, mDuration);
+ }
+
+ /** @hide */
+ @NonNull
+ @Override
+ public StepSegment scaleLinearly(float scaleFactor) {
+ if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0) {
+ return this;
+ }
+ float newAmplitude = VibrationEffect.scaleLinearly(mAmplitude, scaleFactor);
+ if (Float.compare(newAmplitude, mAmplitude) == 0) {
+ return this;
+ }
+ return new StepSegment(newAmplitude, mFrequencyHz, mDuration);
}
/** @hide */
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index 17ac36f3ab37..e1fb4e361008 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -96,6 +96,9 @@ public abstract class VibrationEffectSegment implements Parcelable {
/**
* Scale the segment intensity with the given factor.
*
+ * <p> This scale is not necessarily linear and may apply a gamma correction to the scale
+ * factor before using it.
+ *
* @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
* scale down the intensity, values larger than 1 will scale up
*
@@ -105,6 +108,17 @@ public abstract class VibrationEffectSegment implements Parcelable {
public abstract <T extends VibrationEffectSegment> T scale(float scaleFactor);
/**
+ * Performs a linear scaling on the segment intensity with the given factor.
+ *
+ * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
+ * scale down the intensity, values larger than 1 will scale up
+ *
+ * @hide
+ */
+ @NonNull
+ public abstract <T extends VibrationEffectSegment> T scaleLinearly(float scaleFactor);
+
+ /**
* Applies given effect strength to prebaked effects.
*
* @param effectStrength new effect strength to be applied, one of
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 9218cb8f497d..de7008b19f54 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -130,3 +130,11 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "ignore_process_text"
+ namespace: "permissions"
+ description: "Ignore activities that handle PROCESS_TEXT in TextView"
+ bug: "325356776"
+}
+
diff --git a/core/java/android/print/pdf/TEST_MAPPING b/core/java/android/print/pdf/TEST_MAPPING
deleted file mode 100644
index d763598f5ba0..000000000000
--- a/core/java/android/print/pdf/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit": [
- {
- "name": "CtsPdfTestCases"
- }
- ]
-}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ec4d5876070a..50adc40e719d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12481,6 +12481,24 @@ public final class Settings {
public static void setLocationProviderEnabled(ContentResolver cr,
String provider, boolean enabled) {
}
+
+ /**
+ * List of system components that support restore in a V-> U OS downgrade but do not have
+ * RestoreAnyVersion set to true. Value set before system restore.
+ * This setting is not B&Rd
+ * List is stored as a comma-separated string of package names e.g. "a,b,c"
+ * @hide
+ */
+ public static final String V_TO_U_RESTORE_ALLOWLIST = "v_to_u_restore_allowlist";
+
+ /**
+ * List of system components that have RestoreAnyVersion set to true but do not support
+ * restore in a V-> U OS downgrade. Value set before system restore.
+ * This setting is not B&Rd
+ * List is stored as a comma-separated string of package names e.g. "a,b,c"
+ * @hide
+ */
+ public static final String V_TO_U_RESTORE_DENYLIST = "v_to_u_restore_denylist";
}
/**
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 15fb6ccf82fa..d9ca935b35b3 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -216,7 +216,7 @@ public class ZenModeConfig implements Parcelable {
private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
private static final String ALLOW_ATT_CONV = "convos";
private static final String ALLOW_ATT_CONV_FROM = "convosFrom";
- private static final String ALLOW_ATT_CHANNELS = "priorityChannels";
+ private static final String ALLOW_ATT_CHANNELS = "priorityChannelsAllowed";
private static final String POLICY_USER_MODIFIED_FIELDS = "policyUserModifiedFields";
private static final String DISALLOW_TAG = "disallow";
private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects";
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index aa47d3a5c2af..786d768bc55b 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -673,10 +673,6 @@ public final class ZenPolicy implements Parcelable {
mZenPolicy.mPriorityMessages = PEOPLE_TYPE_NONE;
mZenPolicy.mPriorityCalls = PEOPLE_TYPE_NONE;
mZenPolicy.mConversationSenders = CONVERSATION_SENDERS_NONE;
-
- if (Flags.modesApi()) {
- mZenPolicy.mAllowChannels = CHANNEL_POLICY_NONE;
- }
return this;
}
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index 446fe3de6482..c5acc2ceb968 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -1,4 +1,5 @@
package: "android.service.notification"
+container: "system"
flag {
name: "ranking_update_ashmem"
@@ -12,6 +13,7 @@ flag {
namespace: "systemui"
description: "This flag controls the redacting of sensitive notifications from untrusted NotificationListenerServices"
bug: "306271190"
+ is_exported: true
}
flag {
diff --git a/core/java/android/service/voice/flags/flags.aconfig b/core/java/android/service/voice/flags/flags.aconfig
index f870db5f95f0..22e8cddbfdb8 100644
--- a/core/java/android/service/voice/flags/flags.aconfig
+++ b/core/java/android/service/voice/flags/flags.aconfig
@@ -16,7 +16,7 @@ flag {
flag {
name: "allow_foreground_activities_in_on_show"
- namespace: "voice_interaction_session"
+ namespace: "machine_learning"
description: "This flag allows providing foreground app component along with onShow args."
bug: "319409708"
}
diff --git a/core/java/android/service/wearable/IWearableSensingService.aidl b/core/java/android/service/wearable/IWearableSensingService.aidl
index 22d8fda9cb8e..dffadf0cda70 100644
--- a/core/java/android/service/wearable/IWearableSensingService.aidl
+++ b/core/java/android/service/wearable/IWearableSensingService.aidl
@@ -28,7 +28,7 @@ import android.os.SharedMemory;
* @hide
*/
oneway interface IWearableSensingService {
- void provideSecureWearableConnection(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
+ void provideSecureConnection(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback);
void registerDataRequestObserver(int dataType, in RemoteCallback dataRequestCallback, int dataRequestObserverId, in String packageName, in RemoteCallback statusCallback);
diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java
index 808c3ae7b6bc..a2770172d3ca 100644
--- a/core/java/android/service/wearable/WearableSensingService.java
+++ b/core/java/android/service/wearable/WearableSensingService.java
@@ -112,11 +112,11 @@ public abstract class WearableSensingService extends Service {
return new IWearableSensingService.Stub() {
/** {@inheritDoc} */
@Override
- public void provideSecureWearableConnection(
+ public void provideSecureConnection(
ParcelFileDescriptor secureWearableConnection, RemoteCallback callback) {
Objects.requireNonNull(secureWearableConnection);
Consumer<Integer> consumer = createWearableStatusConsumer(callback);
- WearableSensingService.this.onSecureWearableConnectionProvided(
+ WearableSensingService.this.onSecureConnectionProvided(
secureWearableConnection, consumer);
}
@@ -311,12 +311,12 @@ public abstract class WearableSensingService extends Service {
/**
* Called when a secure connection to the wearable is available. See {@link
- * WearableSensingManager#provideWearableConnection(ParcelFileDescriptor, Executor, Consumer)}
+ * WearableSensingManager#provideConnection(ParcelFileDescriptor, Executor, Consumer)}
* for details about the secure connection.
*
* <p>When the {@code secureWearableConnection} is closed, the system will send a {@link
* WearableSensingManager#STATUS_CHANNEL_ERROR} status code to the status consumer provided by
- * the caller of {@link WearableSensingManager#provideWearableConnection(ParcelFileDescriptor,
+ * the caller of {@link WearableSensingManager#provideConnection(ParcelFileDescriptor,
* Executor, Consumer)}.
*
* <p>The implementing class should override this method. It should return an appropriate status
@@ -327,7 +327,7 @@ public abstract class WearableSensingService extends Service {
*/
@FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
@BinderThread
- public void onSecureWearableConnectionProvided(
+ public void onSecureConnectionProvided(
@NonNull ParcelFileDescriptor secureWearableConnection,
@NonNull Consumer<Integer> statusConsumer) {
statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 8e52af3fb57b..8dee4b19c6d3 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -16,6 +16,7 @@
package android.text;
+import static com.android.graphics.hwui.flags.Flags.highContrastTextLuminance;
import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION;
@@ -28,7 +29,9 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.BlendMode;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
@@ -46,7 +49,9 @@ import android.text.style.ReplacementSpan;
import android.text.style.TabStopSpan;
import android.widget.TextView;
+import com.android.graphics.hwui.flags.Flags;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
@@ -480,9 +485,23 @@ public abstract class Layout {
int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
if (lastLine < 0) return;
- drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
- cursorOffsetVertical, firstLine, lastLine);
+ if (shouldDrawHighlightsOnTop(canvas)) {
+ drawBackground(canvas, firstLine, lastLine);
+ } else {
+ drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
+ cursorOffsetVertical, firstLine, lastLine);
+ }
+
drawText(canvas, firstLine, lastLine);
+
+ // Since high contrast text draws a solid rectangle background behind the text, it covers up
+ // the highlights and selections. In this case we draw over the top of the text with a
+ // blend mode that ensures the text stays high-contrast.
+ if (shouldDrawHighlightsOnTop(canvas)) {
+ drawHighlights(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
+ cursorOffsetVertical, firstLine, lastLine);
+ }
+
if (leftShift != 0) {
// Manually translate back to the original position because of b/324498002, using
// save/restore disappears the toggle switch drawables.
@@ -490,6 +509,19 @@ public abstract class Layout {
}
}
+ private static boolean shouldDrawHighlightsOnTop(Canvas canvas) {
+ return Flags.highContrastTextSmallTextRect() && canvas.isHighContrastTextEnabled();
+ }
+
+ private static Paint setToHighlightPaint(Paint p, BlendMode blendMode, Paint outPaint) {
+ if (p == null) return null;
+ outPaint.set(p);
+ outPaint.setBlendMode(blendMode);
+ // Yellow for maximum contrast
+ outPaint.setColor(Color.YELLOW);
+ return outPaint;
+ }
+
/**
* Draw text part of this layout.
*
@@ -542,11 +574,28 @@ public abstract class Layout {
int firstLine,
int lastLine) {
drawBackground(canvas, firstLine, lastLine);
+ drawHighlights(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
+ cursorOffsetVertical, firstLine, lastLine);
+ }
+
+ /**
+ * @hide public for Editor.java
+ */
+ public void drawHighlights(
+ @NonNull Canvas canvas,
+ @Nullable List<Path> highlightPaths,
+ @Nullable List<Paint> highlightPaints,
+ @Nullable Path selectionPath,
+ @Nullable Paint selectionPaint,
+ int cursorOffsetVertical,
+ int firstLine,
+ int lastLine) {
if (highlightPaths == null && highlightPaints == null) {
return;
}
if (cursorOffsetVertical != 0) canvas.translate(0, cursorOffsetVertical);
try {
+ BlendMode blendMode = determineHighContrastHighlightBlendMode(canvas);
if (highlightPaths != null) {
if (highlightPaints == null) {
throw new IllegalArgumentException(
@@ -559,7 +608,12 @@ public abstract class Layout {
}
for (int i = 0; i < highlightPaths.size(); ++i) {
final Path highlight = highlightPaths.get(i);
- final Paint highlightPaint = highlightPaints.get(i);
+ Paint highlightPaint = highlightPaints.get(i);
+ if (shouldDrawHighlightsOnTop(canvas)) {
+ highlightPaint = setToHighlightPaint(highlightPaint, blendMode,
+ mWorkPlainPaint);
+ }
+
if (highlight != null) {
canvas.drawPath(highlight, highlightPaint);
}
@@ -567,6 +621,10 @@ public abstract class Layout {
}
if (selectionPath != null) {
+ if (shouldDrawHighlightsOnTop(canvas)) {
+ selectionPaint = setToHighlightPaint(selectionPaint, blendMode,
+ mWorkPlainPaint);
+ }
canvas.drawPath(selectionPath, selectionPaint);
}
} finally {
@@ -574,6 +632,31 @@ public abstract class Layout {
}
}
+ @Nullable
+ private BlendMode determineHighContrastHighlightBlendMode(Canvas canvas) {
+ if (!shouldDrawHighlightsOnTop(canvas)) {
+ return null;
+ }
+
+ return isHighContrastTextDark() ? BlendMode.MULTIPLY : BlendMode.DIFFERENCE;
+ }
+
+ private boolean isHighContrastTextDark() {
+ // High-contrast text mode
+ // Determine if the text is black-on-white or white-on-black, so we know what blendmode will
+ // give the highest contrast and most realistic text color.
+ // This equation should match the one in libs/hwui/hwui/DrawTextFunctor.h
+ if (highContrastTextLuminance()) {
+ var lab = new double[3];
+ ColorUtils.colorToLAB(mPaint.getColor(), lab);
+ return lab[0] < 0.5;
+ } else {
+ var color = mPaint.getColor();
+ int channelSum = Color.red(color) + Color.green(color) + Color.blue(color);
+ return channelSum < (128 * 3);
+ }
+ }
+
private boolean isJustificationRequired(int lineNum) {
if (mJustificationMode == JUSTIFICATION_MODE_NONE) return false;
final int lineEnd = getLineEnd(lineNum);
@@ -3396,7 +3479,8 @@ public abstract class Layout {
private CharSequence mText;
@UnsupportedAppUsage
private TextPaint mPaint;
- private TextPaint mWorkPaint = new TextPaint();
+ private final TextPaint mWorkPaint = new TextPaint();
+ private final Paint mWorkPlainPaint = new Paint();
private int mWidth;
private Alignment mAlignment = Alignment.ALIGN_NORMAL;
private float mSpacingMult;
diff --git a/core/java/android/tracing/flags.aconfig b/core/java/android/tracing/flags.aconfig
index c6e8844bc47a..b1bca96c5c09 100644
--- a/core/java/android/tracing/flags.aconfig
+++ b/core/java/android/tracing/flags.aconfig
@@ -12,4 +12,5 @@ flag {
namespace: "windowing_tools"
description: "Migrate protolog to Perfetto"
bug: "276432490"
+ is_fixed_read_only: true
}
diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java
index e679f2998ca1..ca2e56d383e5 100644
--- a/core/java/android/view/BatchedInputEventReceiver.java
+++ b/core/java/android/view/BatchedInputEventReceiver.java
@@ -19,6 +19,7 @@ package android.view;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Handler;
import android.os.Looper;
+import android.os.Trace;
/**
* Similar to {@link InputEventReceiver}, but batches events to vsync boundaries when possible.
@@ -42,6 +43,8 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
super(inputChannel, looper);
mChoreographer = choreographer;
mBatchingEnabled = true;
+ traceBoolVariable("mBatchingEnabled", mBatchingEnabled);
+ traceBoolVariable("mBatchedInputScheduled", mBatchedInputScheduled);
mHandler = new Handler(looper);
}
@@ -71,6 +74,7 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
}
mBatchingEnabled = batchingEnabled;
+ traceBoolVariable("mBatchingEnabled", mBatchingEnabled);
mHandler.removeCallbacks(mConsumeBatchedInputEvents);
if (!batchingEnabled) {
unscheduleBatchedInput();
@@ -81,6 +85,7 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
protected void doConsumeBatchedInput(long frameTimeNanos) {
if (mBatchedInputScheduled) {
mBatchedInputScheduled = false;
+ traceBoolVariable("mBatchedInputScheduled", mBatchedInputScheduled);
if (consumeBatchedInputEvents(frameTimeNanos) && frameTimeNanos != -1) {
// If we consumed a batch here, we want to go ahead and schedule the
// consumption of batched input events on the next frame. Otherwise, we would
@@ -95,6 +100,7 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
private void scheduleBatchedInput() {
if (!mBatchedInputScheduled) {
mBatchedInputScheduled = true;
+ traceBoolVariable("mBatchedInputScheduled", mBatchedInputScheduled);
mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, mBatchedInputRunnable, null);
}
}
@@ -102,11 +108,18 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
private void unscheduleBatchedInput() {
if (mBatchedInputScheduled) {
mBatchedInputScheduled = false;
+ traceBoolVariable("mBatchedInputScheduled", mBatchedInputScheduled);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_INPUT, mBatchedInputRunnable, null);
}
}
+ // @TODO(b/311142655): Delete this temporary tracing. It's only used here to debug a very
+ // specific issue.
+ private void traceBoolVariable(String name, boolean value) {
+ Trace.traceCounter(Trace.TRACE_TAG_INPUT, name, value ? 1 : 0);
+ }
+
private final class BatchedInputRunnable implements Runnable {
@Override
public void run() {
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 676b903472c5..00657ea35e02 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -38,6 +39,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.function.Consumer;
/**
* Initiates handwriting mode once it detects stylus movement in handwritable areas.
@@ -415,27 +417,55 @@ public class HandwritingInitiator {
*/
@VisibleForTesting
public boolean tryAcceptStylusHandwritingDelegation(@NonNull View view) {
+ if (Flags.useZeroJankProxy()) {
+ tryAcceptStylusHandwritingDelegationAsync(view);
+ } else {
+ return tryAcceptStylusHandwritingDelegationInternal(view);
+ }
+ return false;
+ }
+
+ private boolean tryAcceptStylusHandwritingDelegationInternal(@NonNull View view) {
String delegatorPackageName =
view.getAllowedHandwritingDelegatorPackageName();
if (delegatorPackageName == null) {
delegatorPackageName = view.getContext().getOpPackageName();
}
if (mImm.acceptStylusHandwritingDelegation(view, delegatorPackageName)) {
- if (mState != null) {
- mState.mHasInitiatedHandwriting = true;
- mState.mShouldInitHandwriting = false;
- }
- if (view instanceof TextView) {
- ((TextView) view).hideHint();
- }
- // A handwriting delegate view is accepted and handwriting starts; hide the
- // hover icon.
- mShowHoverIconForConnectedView = false;
+ onDelegationAccepted(view);
return true;
}
return false;
}
+ @FlaggedApi(Flags.FLAG_USE_ZERO_JANK_PROXY)
+ private void tryAcceptStylusHandwritingDelegationAsync(@NonNull View view) {
+ String delegatorPackageName =
+ view.getAllowedHandwritingDelegatorPackageName();
+ if (delegatorPackageName == null) {
+ delegatorPackageName = view.getContext().getOpPackageName();
+ }
+ Consumer<Boolean> consumer = delegationAccepted -> {
+ if (delegationAccepted) {
+ onDelegationAccepted(view);
+ }
+ };
+ mImm.acceptStylusHandwritingDelegation(view, delegatorPackageName, view::post, consumer);
+ }
+
+ private void onDelegationAccepted(View view) {
+ if (mState != null) {
+ mState.mHasInitiatedHandwriting = true;
+ mState.mShouldInitHandwriting = false;
+ }
+ if (view instanceof TextView) {
+ ((TextView) view).hideHint();
+ }
+ // A handwriting delegate view is accepted and handwriting starts; hide the
+ // hover icon.
+ mShowHoverIconForConnectedView = false;
+ }
+
/**
* Notify that the handwriting area for the given view might be updated.
* @param view the view whose handwriting area might be updated.
diff --git a/core/java/android/view/ISensitiveContentProtectionManager.aidl b/core/java/android/view/ISensitiveContentProtectionManager.aidl
new file mode 100644
index 000000000000..c135ae4b6a95
--- /dev/null
+++ b/core/java/android/view/ISensitiveContentProtectionManager.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.os.IBinder;
+
+/**
+ * @hide
+ */
+oneway interface ISensitiveContentProtectionManager {
+ /**
+ * Block projection for a package's window when the window is showing sensitive content on
+ * the screen, the projection is unblocked when the window no more shows sensitive content.
+ *
+ * @param windowToken window where the content is shown.
+ * @param packageName package name.
+ * @param isShowingSensitiveContent whether the window is showing sensitive content.
+ */
+ void setSensitiveContentProtection(in IBinder windowToken, in String packageName,
+ in boolean isShowingSensitiveContent);
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c475f6babbf1..51d7caacd4b7 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -69,11 +69,11 @@ import android.view.SurfaceControl;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
import android.window.AddToSurfaceSyncGroupResult;
+import android.window.IGlobalDragListener;
import android.window.IScreenRecordingCallback;
import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
import android.window.ITrustedPresentationListener;
-import android.window.IUnhandledDragListener;
import android.window.InputTransferToken;
import android.window.ScreenCapture;
import android.window.TrustedPresentationThresholds;
@@ -1094,10 +1094,9 @@ interface IWindowManager
void unregisterScreenRecordingCallback(IScreenRecordingCallback callback);
/**
- * Sets the listener to be called back when a cross-window drag and drop operation is unhandled
- * (ie. not handled by any window which can handle the drag).
+ * Sets the listener to be called back when a cross-window drag and drop operation happens.
*/
- void setUnhandledDragListener(IUnhandledDragListener listener);
+ void setGlobalDragListener(IGlobalDragListener listener);
boolean transferTouchGesture(in InputTransferToken transferFromToken,
in InputTransferToken transferToToken);
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 9c430cd4acb4..2cc05b0bc4b0 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -271,12 +271,29 @@ public abstract class InputEventReceiver {
return mInputChannel.getToken();
}
+ private String getShortDescription(InputEvent event) {
+ if (event instanceof MotionEvent motion) {
+ return "MotionEvent " + MotionEvent.actionToString(motion.getAction()) + " deviceId="
+ + motion.getDeviceId() + " source=0x"
+ + Integer.toHexString(motion.getSource()) + " historySize="
+ + motion.getHistorySize();
+ } else if (event instanceof KeyEvent key) {
+ return "KeyEvent " + KeyEvent.actionToString(key.getAction())
+ + " deviceId=" + key.getDeviceId();
+ } else {
+ Log.wtf(TAG, "Illegal InputEvent type: " + event);
+ return "InputEvent";
+ }
+ }
+
// Called from native code.
@SuppressWarnings("unused")
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void dispatchInputEvent(int seq, InputEvent event) {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "dispatchInputEvent " + getShortDescription(event));
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cd6d79c81b98..06dc2755bb48 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -85,6 +85,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.credentials.CredentialManager;
+import android.credentials.CredentialOption;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
import android.credentials.GetCredentialResponse;
@@ -129,6 +130,7 @@ import android.os.SystemClock;
import android.os.Trace;
import android.os.Vibrator;
import android.os.vibrator.Flags;
+import android.service.credentials.CredentialProviderService;
import android.sysprop.DisplayProperties;
import android.text.InputType;
import android.text.TextUtils;
@@ -1047,7 +1049,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@Nullable
private ViewCredentialHandler mViewCredentialHandler;
-
/** Used to avoid computing the full strings each time when layout tracing is enabled. */
@Nullable
private ViewTraversalTracingStrings mTracingStrings;
@@ -7035,6 +7036,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
Preconditions.checkNotNull(request, "request must not be null");
Preconditions.checkNotNull(callback, "request must not be null");
+ for (CredentialOption option : request.getCredentialOptions()) {
+ ArrayList<AutofillId> ids = option.getCandidateQueryData()
+ .getParcelableArrayList(
+ CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class);
+ ids = ids != null ? ids : new ArrayList<>();
+ if (!ids.contains(getAutofillId())) {
+ ids.add(getAutofillId());
+ }
+ option.getCandidateQueryData()
+ .putParcelableArrayList(CredentialProviderService.EXTRA_AUTOFILL_ID, ids);
+ }
mViewCredentialHandler = new ViewCredentialHandler(request, callback);
}
@@ -9915,6 +9927,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * @hide
+ */
+ public void onGetCredentialResponse(GetCredentialResponse response) {
+ if (getCredentialManagerCallback() == null) {
+ Log.w(AUTOFILL_LOG_TAG, "onGetCredentialResponse called but no callback found");
+ return;
+ }
+ getCredentialManagerCallback().onResult(response);
+ }
+
+ /**
* Gets the unique, logical identifier of this view in the activity, for autofill purposes.
*
* <p>The autofill id is created on demand, unless it is explicitly set by
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 83683caa2ea8..364c94f7f92f 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -50,6 +50,7 @@ import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.credentials.GetCredentialResponse;
import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.Build;
@@ -2925,6 +2926,65 @@ public final class AutofillManager {
}
}
+ private void onGetCredentialResponse(int sessionId, AutofillId id,
+ GetCredentialResponse response) {
+ synchronized (mLock) {
+ if (sessionId != mSessionId) {
+ Log.w(TAG, "onGetCredentialResponse afm sessionIds don't match");
+ return;
+ }
+
+ final AutofillClient client = getClient();
+ if (client == null) {
+ Log.w(TAG, "onGetCredentialResponse afm client id null");
+ return;
+ }
+ ArrayList<AutofillId> failedIds = new ArrayList<>();
+ final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
+ Helper.toArray(new ArrayList<>(Collections.singleton(id))));
+ if (views == null || views.length == 0) {
+ Log.w(TAG, "onGetCredentialResponse afm client view not found");
+ return;
+ }
+
+ final View view = views[0];
+ if (view == null) {
+ Log.i(TAG, "onGetCredentialResponse View is null");
+
+ // Most likely view has been removed after the initial request was sent to the
+ // the service; this is fine, but we need to update the view status in the
+ // server side so it can be triggered again.
+ Log.d(TAG, "onGetCredentialResponse(): no View with id " + id);
+ failedIds.add(id);
+ }
+ if (id.isVirtualInt()) {
+ Log.i(TAG, "onGetCredentialResponse afm client id is virtual");
+ // TODO(b/326314286): Handle virtual views
+ } else {
+ Log.i(TAG, "onGetCredentialResponse afm client id is NOT virtual");
+ view.onGetCredentialResponse(response);
+ }
+ handleFailedIdsLocked(failedIds);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void handleFailedIdsLocked(ArrayList<AutofillId> failedIds) {
+ if (failedIds != null && !failedIds.isEmpty()) {
+ if (sVerbose) {
+ Log.v(TAG, "autofill(): total failed views: " + failedIds);
+ }
+ try {
+ mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
+ } catch (RemoteException e) {
+ // In theory, we could ignore this error since it's not a big deal, but
+ // in reality, we rather crash the app anyways, as the failure could be
+ // a consequence of something going wrong on the server side...
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
boolean hideHighlight) {
synchronized (mLock) {
@@ -2991,19 +3051,7 @@ public final class AutofillManager {
}
}
- if (failedIds != null) {
- if (sVerbose) {
- Log.v(TAG, "autofill(): total failed views: " + failedIds);
- }
- try {
- mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
- } catch (RemoteException e) {
- // In theory, we could ignore this error since it's not a big deal, but
- // in reality, we rather crash the app anyways, as the failure could be
- // a consequence of something going wrong on the server side...
- throw e.rethrowFromSystemServer();
- }
- }
+ handleFailedIdsLocked(failedIds);
if (virtualValues != null) {
for (int i = 0; i < virtualValues.size(); i++) {
@@ -3431,6 +3479,10 @@ public final class AutofillManager {
if (view == null) {
return false;
}
+ if (view.getViewCredentialHandler() != null) {
+ return true;
+ }
+
String[] hints = view.getAutofillHints();
if (hints == null) {
return false;
@@ -4321,6 +4373,15 @@ public final class AutofillManager {
}
@Override
+ public void onGetCredentialResponse(int sessionId, AutofillId id,
+ GetCredentialResponse response) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.post(() -> afm.onGetCredentialResponse(sessionId, id, response));
+ }
+ }
+
+ @Override
public void autofillContent(int sessionId, AutofillId id, ClipData content) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 917a974f992d..e838027a9ae1 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -22,6 +22,7 @@ import android.content.ClipData;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentSender;
+import android.credentials.GetCredentialResponse;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.autofill.AutofillId;
@@ -48,6 +49,9 @@ oneway interface IAutoFillManagerClient {
void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values,
boolean hideHighlight);
+ void onGetCredentialResponse(int sessionId, in AutofillId id,
+ in GetCredentialResponse response);
+
/**
* Autofills the activity with rich content data (e.g. an image) from a dataset.
*/
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index 88ca2a48d2f4..491b0e349cde 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -34,6 +34,7 @@ import android.view.WindowManager;
import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInputMethodClient;
@@ -587,6 +588,28 @@ final class IInputMethodManagerGlobalInvoker {
}
}
+ /** Returns {@code true} if method is invoked */
+ @AnyThread
+ static boolean acceptStylusHandwritingDelegationAsync(
+ @NonNull IInputMethodClient client,
+ @UserIdInt int userId,
+ @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags,
+ @NonNull IBooleanListener callback) {
+ final IInputMethodManager service = getService();
+ if (service == null) {
+ return false;
+ }
+ try {
+ service.acceptStylusHandwritingDelegationAsync(
+ client, userId, delegatePackageName, delegatorPackageName, flags, callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return true;
+ }
+
@AnyThread
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
static boolean isStylusHandwritingAvailableAsUser(
diff --git a/core/java/android/view/inputmethod/InputBinding.java b/core/java/android/view/inputmethod/InputBinding.java
index 2bfeb5abb395..fedee9de1372 100644
--- a/core/java/android/view/inputmethod/InputBinding.java
+++ b/core/java/android/view/inputmethod/InputBinding.java
@@ -19,11 +19,13 @@ package android.view.inputmethod;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
/**
* Information given to an {@link InputMethod} about a client connecting
* to it.
*/
+@RavenwoodKeepWholeClass
public final class InputBinding implements Parcelable {
static final String TAG = "InputBinding";
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 72125ba999ad..3bce155049c8 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -109,6 +109,7 @@ import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IInputMethodClient;
import com.android.internal.inputmethod.IInputMethodSession;
@@ -461,8 +462,8 @@ public final class InputMethodManager {
* Flag indicating that views from the default home screen ({@link Intent#CATEGORY_HOME}) may
* act as a handwriting delegator for the delegate editor view. If set, views from the home
* screen package will be trusted for handwriting delegation, in addition to views in the {@code
- * delegatorPackageName} passed to {@link #acceptStylusHandwritingDelegation(View, String,
- * int)}.
+ * delegatorPackageName} passed to
+ * {@link #acceptStylusHandwritingDelegation(View, String, int, Executor, Consumer)} .
*/
@FlaggedApi(FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR)
public static final int HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED = 0x0001;
@@ -2518,16 +2519,46 @@ public final class InputMethodManager {
view, /* delegatorPackageName= */ null, /* handwritingDelegateFlags= */ 0);
}
+ private void startStylusHandwritingInternalAsync(
+ @NonNull View view, @Nullable String delegatorPackageName,
+ @HandwritingDelegateFlags int handwritingDelegateFlags,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+ Objects.requireNonNull(view);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ startStylusHandwritingInternal(
+ view, delegatorPackageName, handwritingDelegateFlags, executor, callback);
+ }
+
+ private void sendFailureCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> callback) {
+ if (executor == null || callback == null) {
+ return;
+ }
+ executor.execute(() -> callback.accept(false));
+ }
+
private boolean startStylusHandwritingInternal(
@NonNull View view, @Nullable String delegatorPackageName,
@HandwritingDelegateFlags int handwritingDelegateFlags) {
+ return startStylusHandwritingInternal(
+ view, delegatorPackageName, handwritingDelegateFlags,
+ null /* executor */, null /* callback */);
+ }
+
+ private boolean startStylusHandwritingInternal(
+ @NonNull View view, @Nullable String delegatorPackageName,
+ @HandwritingDelegateFlags int handwritingDelegateFlags, Executor executor,
+ Consumer<Boolean> callback) {
Objects.requireNonNull(view);
+ boolean useCallback = callback != null;
// Re-dispatch if there is a context mismatch.
final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
if (fallbackImm != null) {
fallbackImm.startStylusHandwritingInternal(
- view, delegatorPackageName, handwritingDelegateFlags);
+ view, delegatorPackageName, handwritingDelegateFlags, executor, callback);
}
boolean useDelegation = !TextUtils.isEmpty(delegatorPackageName);
@@ -2537,21 +2568,40 @@ public final class InputMethodManager {
if (!hasServedByInputMethodLocked(view)) {
Log.w(TAG,
"Ignoring startStylusHandwriting as view=" + view + " is not served.");
+ sendFailureCallback(executor, callback);
return false;
}
if (view.getViewRootImpl() != mCurRootView) {
Log.w(TAG,
"Ignoring startStylusHandwriting: View's window does not have focus.");
+ sendFailureCallback(executor, callback);
return false;
}
if (useDelegation) {
- return IInputMethodManagerGlobalInvoker.acceptStylusHandwritingDelegation(
- mClient, UserHandle.myUserId(), view.getContext().getOpPackageName(),
- delegatorPackageName, handwritingDelegateFlags);
+ if (useCallback) {
+ IBooleanListener listener = new IBooleanListener.Stub() {
+ @Override
+ public void onResult(boolean value) {
+ executor.execute(() -> {
+ callback.accept(value);
+ });
+ }
+ };
+ if (!IInputMethodManagerGlobalInvoker.acceptStylusHandwritingDelegationAsync(
+ mClient, UserHandle.myUserId(), view.getContext().getOpPackageName(),
+ delegatorPackageName, handwritingDelegateFlags, listener)) {
+ sendFailureCallback(executor, callback);
+ }
+ return true;
+ } else {
+ return IInputMethodManagerGlobalInvoker.acceptStylusHandwritingDelegation(
+ mClient, UserHandle.myUserId(), view.getContext().getOpPackageName(),
+ delegatorPackageName, handwritingDelegateFlags);
+ }
} else {
IInputMethodManagerGlobalInvoker.startStylusHandwriting(mClient);
+ return false;
}
- return false;
}
}
@@ -2788,6 +2838,7 @@ public final class InputMethodManager {
* #prepareStylusHandwritingDelegation(View, String)} and delegation is accepted
* @see #prepareStylusHandwritingDelegation(View, String)
* @see #acceptStylusHandwritingDelegation(View)
+ * TODO (b/293640003): deprecate this method once flag is enabled.
*/
// TODO(b/300979854): Once connectionless APIs are finalised, update documentation to add:
// <p>Otherwise, if the delegator view previously started delegation using {@link
@@ -2805,6 +2856,36 @@ public final class InputMethodManager {
/**
* Accepts and starts a stylus handwriting session on the delegate view, if handwriting
+ * initiation delegation was previously requested using
+ * {@link #prepareStylusHandwritingDelegation(View, String)} from the delegator and the view
+ * belongs to a specified delegate package.
+ *
+ * @param delegateView delegate view capable of receiving input via {@link InputConnection}
+ * on which {@link #startStylusHandwriting(View)} will be called.
+ * @param delegatorPackageName package name of the delegator that handled initial stylus stroke.
+ * @param executor The executor to run the callback on.
+ * @param callback Consumer callback that provides {@code true} if view belongs to allowed
+ * delegate package declared in
+ * {@link #prepareStylusHandwritingDelegation(View, String)} and handwriting
+ * session can start.
+ * @see #prepareStylusHandwritingDelegation(View, String)
+ * @see #acceptStylusHandwritingDelegation(View)
+ */
+ @FlaggedApi(Flags.FLAG_USE_ZERO_JANK_PROXY)
+ public void acceptStylusHandwritingDelegation(
+ @NonNull View delegateView, @NonNull String delegatorPackageName,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+ Objects.requireNonNull(delegatorPackageName);
+ int flags = 0;
+ if (Flags.homeScreenHandwritingDelegator()) {
+ flags = delegateView.getHandwritingDelegateFlags();
+ }
+ startStylusHandwritingInternalAsync(
+ delegateView, delegatorPackageName, flags, executor, callback);
+ }
+
+ /**
+ * Accepts and starts a stylus handwriting session on the delegate view, if handwriting
* initiation delegation was previously requested using {@link
* #prepareStylusHandwritingDelegation(View, String)} from the delegator and the view belongs to
* a specified delegate package.
@@ -2815,6 +2896,8 @@ public final class InputMethodManager {
* @param delegateView delegate view capable of receiving input via {@link InputConnection}
* @param delegatorPackageName package name of the delegator that handled initial stylus stroke.
* @param flags {@link #HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED} or {@code 0}
+ * @param executor The executor to run the callback on.
+ * @param callback {@code true>} would be received if delegation was accepted.
* @return {@code true} if view belongs to allowed delegate package declared in {@link
* #prepareStylusHandwritingDelegation(View, String)} and delegation is accepted
* @see #prepareStylusHandwritingDelegation(View, String)
@@ -2827,13 +2910,16 @@ public final class InputMethodManager {
// session to the delegate view.
// @see #startConnectionlessStylusHandwritingForDelegation(View, ResultReceiver,
// CursorAnchorInfo, String)
+ //
@FlaggedApi(FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR)
- public boolean acceptStylusHandwritingDelegation(
+ public void acceptStylusHandwritingDelegation(
@NonNull View delegateView, @NonNull String delegatorPackageName,
- @HandwritingDelegateFlags int flags) {
+ @HandwritingDelegateFlags int flags, @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> callback) {
Objects.requireNonNull(delegatorPackageName);
- return startStylusHandwritingInternal(delegateView, delegatorPackageName, flags);
+ startStylusHandwritingInternal(
+ delegateView, delegatorPackageName, flags, executor, callback);
}
/**
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 57d268ced6f4..139ebc38706e 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -19,6 +19,8 @@ package android.widget;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
import static android.widget.TextView.ACCESSIBILITY_ACTION_SMART_START_ID;
+import static com.android.graphics.hwui.flags.Flags.highContrastTextSmallTextRect;
+
import android.R;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
@@ -2151,8 +2153,15 @@ public class Editor {
int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
if (lastLine < 0) return;
- layout.drawWithoutText(canvas, highlightPaths, highlightPaints, selectionHighlight,
- selectionHighlightPaint, cursorOffsetVertical, firstLine, lastLine);
+ boolean shouldDrawHighlightsOnTop = highContrastTextSmallTextRect()
+ && canvas.isHighContrastTextEnabled();
+
+ if (!shouldDrawHighlightsOnTop) {
+ layout.drawWithoutText(canvas, highlightPaths, highlightPaints, selectionHighlight,
+ selectionHighlightPaint, cursorOffsetVertical, firstLine, lastLine);
+ } else {
+ layout.drawBackground(canvas, firstLine, lastLine);
+ }
if (layout instanceof DynamicLayout) {
if (mTextRenderNodes == null) {
@@ -2226,6 +2235,11 @@ public class Editor {
// Boring layout is used for empty and hint text
layout.drawText(canvas, firstLine, lastLine);
}
+
+ if (shouldDrawHighlightsOnTop) {
+ layout.drawHighlights(canvas, highlightPaths, highlightPaints, selectionHighlight,
+ selectionHighlightPaint, cursorOffsetVertical, firstLine, lastLine);
+ }
}
private int drawHardwareAcceleratedInner(Canvas canvas, Layout layout, Path highlight,
diff --git a/core/java/android/window/IUnhandledDragListener.aidl b/core/java/android/window/IGlobalDragListener.aidl
index 52e98952971d..8f2ca02b3c09 100644
--- a/core/java/android/window/IUnhandledDragListener.aidl
+++ b/core/java/android/window/IGlobalDragListener.aidl
@@ -16,14 +16,21 @@
package android.window;
+import android.app.ActivityManager;
import android.view.DragEvent;
import android.window.IUnhandledDragCallback;
/**
- * An interface to a handler for global drags that are not consumed (ie. not handled by any window).
+ * An interface to a handler for global drags.
* {@hide}
*/
-oneway interface IUnhandledDragListener {
+oneway interface IGlobalDragListener {
+ /**
+ * Called when a cross-window drag is handled by another window.
+ * @param taskInfo the task containing the window that consumed the drop
+ */
+ void onCrossWindowDrop(in ActivityManager.RunningTaskInfo taskInfo);
+
/**
* Called when the user finishes the drag gesture but no windows have reported handling the
* drop. The DragEvent is populated with the drag surface for the listener to animate. The
diff --git a/core/java/com/android/internal/app/SetScreenLockDialogActivity.java b/core/java/com/android/internal/app/SetScreenLockDialogActivity.java
new file mode 100644
index 000000000000..93fe37c974b2
--- /dev/null
+++ b/core/java/com/android/internal/app/SetScreenLockDialogActivity.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import static android.Manifest.permission.HIDE_OVERLAY_WINDOWS;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.hardware.biometrics.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
+import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL;
+import static android.provider.Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.RequiresPermission;
+import android.app.AlertDialog;
+import android.app.KeyguardManager;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import com.android.internal.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A dialog shown to the user that prompts them to set the screen lock for the current foreground
+ * user. Should be called from the context of foreground user.
+ */
+public class SetScreenLockDialogActivity extends AlertActivity
+ implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
+ private static final String TAG = "SetScreenLockDialog";
+ public static final String EXTRA_LAUNCH_REASON = "launch_reason";
+ /**
+ * User id associated with the workflow that wants to launch the prompt to set up the
+ * screen lock
+ */
+ public static final String EXTRA_ORIGIN_USER_ID = "origin_user_id";
+ private static final String PACKAGE_NAME = "android";
+ @IntDef(prefix = "LAUNCH_REASON_", value = {
+ LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS,
+ LAUNCH_REASON_DISABLE_QUIET_MODE,
+ LAUNCH_REASON_UNKNOWN,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LaunchReason {
+ }
+ public static final int LAUNCH_REASON_UNKNOWN = -1;
+ public static final int LAUNCH_REASON_DISABLE_QUIET_MODE = 1;
+ public static final int LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS = 2;
+ private @LaunchReason int mReason;
+ private int mOriginUserId;
+
+ @Override
+ @RequiresPermission(HIDE_OVERLAY_WINDOWS)
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (!(android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.showSetScreenLockDialog())) {
+ finish();
+ return;
+ }
+ Intent intent = getIntent();
+ mReason = intent.getIntExtra(EXTRA_LAUNCH_REASON, LAUNCH_REASON_UNKNOWN);
+ mOriginUserId = intent.getIntExtra(EXTRA_ORIGIN_USER_ID, UserHandle.USER_NULL);
+
+ if (mReason == LAUNCH_REASON_UNKNOWN) {
+ Log.e(TAG, "Invalid launch reason: " + mReason);
+ finish();
+ return;
+ }
+
+ final KeyguardManager km = getSystemService(KeyguardManager.class);
+ if (km == null) {
+ Log.e(TAG, "Error fetching keyguard manager");
+ return;
+ }
+ if (km.isDeviceSecure()) {
+ Log.w(TAG, "Closing the activity since screen lock is already set");
+ return;
+ }
+
+ Log.d(TAG, "Launching screen lock setup dialog due to " + mReason);
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.set_up_screen_lock_title)
+ .setOnDismissListener(this)
+ .setPositiveButton(R.string.set_up_screen_lock_action_label, this)
+ .setNegativeButton(R.string.cancel, this);
+ setLaunchUserSpecificMessage(builder);
+ final AlertDialog dialog = builder.create();
+ dialog.create();
+ getWindow().setHideOverlayWindows(true);
+ dialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
+ dialog.show();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ finish();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ Intent setNewLockIntent = new Intent(ACTION_BIOMETRIC_ENROLL);
+ setNewLockIntent.putExtra(EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, DEVICE_CREDENTIAL);
+ startActivity(setNewLockIntent);
+ } else {
+ finish();
+ }
+ }
+
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
+ private void setLaunchUserSpecificMessage(AlertDialog.Builder builder) {
+ if (mReason == LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS) {
+ // Always set private space message if launch reason is specific to private space
+ builder.setMessage(R.string.private_space_set_up_screen_lock_message);
+ return;
+ }
+ final UserManager userManager = getApplicationContext().getSystemService(UserManager.class);
+ if (userManager != null) {
+ UserInfo userInfo = userManager.getUserInfo(mOriginUserId);
+ if (userInfo != null && userInfo.isPrivateProfile()) {
+ builder.setMessage(R.string.private_space_set_up_screen_lock_message);
+ }
+ }
+ }
+
+ /** Returns a basic intent to display the screen lock dialog */
+ public static Intent createBaseIntent(@LaunchReason int launchReason) {
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(PACKAGE_NAME,
+ SetScreenLockDialogActivity.class.getName()));
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(EXTRA_LAUNCH_REASON, launchReason);
+ return intent;
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/IBooleanListener.aidl b/core/java/com/android/internal/inputmethod/IBooleanListener.aidl
new file mode 100644
index 000000000000..8830b1c2f21a
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IBooleanListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+/**
+ * Interface for providing a Boolean result.
+ */
+oneway interface IBooleanListener
+{
+ void onResult(boolean value);
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/protolog/common/LogLevel.java b/core/java/com/android/internal/protolog/common/LogLevel.java
new file mode 100644
index 000000000000..16c34e1f333e
--- /dev/null
+++ b/core/java/com/android/internal/protolog/common/LogLevel.java
@@ -0,0 +1,26 @@
+/*
+ * 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.internal.protolog.common;
+
+public enum LogLevel {
+ DEBUG("d"), VERBOSE("v"), INFO("i"), WARN("w"), ERROR("e"), WTF("wtf");
+
+ public final String shortCode;
+ LogLevel(String shortCode) {
+ this.shortCode = shortCode;
+ }
+}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index b90f8bf5ea00..1f4503a69428 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -24,6 +24,7 @@ import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo;
import android.window.ImeOnBackInvokedDispatcher;
+import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInputMethodClient;
@@ -173,11 +174,16 @@ interface IInputMethodManager {
in String delegatePackageName,
in String delegatorPackageName);
- // TODO(b/293640003): introduce a new API method to provide async way to return boolean.
/** Accepts and starts a stylus handwriting session for the delegate view **/
boolean acceptStylusHandwritingDelegation(in IInputMethodClient client, in int userId,
in String delegatePackageName, in String delegatorPackageName, int flags);
+ /** Accepts and starts a stylus handwriting session for the delegate view and provides result
+ * async **/
+ oneway void acceptStylusHandwritingDelegationAsync(in IInputMethodClient client, in int userId,
+ in String delegatePackageName, in String delegatorPackageName, int flags,
+ in IBooleanListener callback);
+
/** Returns {@code true} if currently selected IME supports Stylus handwriting. */
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)")
diff --git a/core/java/com/android/internal/widget/CallLayout.java b/core/java/com/android/internal/widget/CallLayout.java
index 89f46599322e..c85257578492 100644
--- a/core/java/com/android/internal/widget/CallLayout.java
+++ b/core/java/com/android/internal/widget/CallLayout.java
@@ -31,6 +31,7 @@ import android.view.RemotableViewMethod;
import android.widget.FrameLayout;
import android.widget.RemoteViews;
import android.widget.TextView;
+import android.widget.flags.Flags;
import com.android.internal.R;
@@ -41,7 +42,17 @@ import com.android.internal.R;
public class CallLayout extends FrameLayout {
private final PeopleHelper mPeopleHelper = new PeopleHelper();
+ /**
+ * Layout Color is used for creating CallLayout person avatar.
+ * It will be set on the background thread during CallLayout's inflation
+ * when call_style_set_data_async is enabled.
+ */
private int mLayoutColor;
+ /**
+ * LargeIcon is used for creating CallLayout person avatar.
+ * It will be set on the background thread during CallLayout's inflation
+ * when call_style_set_data_async is enabled.
+ */
private Icon mLargeIcon;
private Person mUser;
@@ -49,7 +60,6 @@ public class CallLayout extends FrameLayout {
private CachingIconView mIcon;
private CachingIconView mConversationIconBadgeBg;
private TextView mConversationText;
- private boolean mSetDataAsyncEnabled = false;
public CallLayout(@NonNull Context context) {
super(context);
@@ -103,7 +113,19 @@ public class CallLayout extends FrameLayout {
return icon;
}
- @RemotableViewMethod
+ /**
+ * async version of {@link CallLayout#setLayoutColor}
+ */
+ public Runnable setLayoutColorAsync(int color) {
+ if (!Flags.callStyleSetDataAsync()) {
+ return () -> setLayoutColor(color);
+ }
+
+ mLayoutColor = color;
+ return () -> {};
+ }
+
+ @RemotableViewMethod(asyncImpl = "setLayoutColorAsync")
public void setLayoutColor(int color) {
mLayoutColor = color;
}
@@ -116,7 +138,19 @@ public class CallLayout extends FrameLayout {
mConversationIconBadgeBg.setImageTintList(ColorStateList.valueOf(color));
}
- @RemotableViewMethod
+ /**
+ * async version of {@link CallLayout#setLargeIcon}
+ */
+ public Runnable setLargeIconAsync(Icon largeIcon) {
+ if (!Flags.callStyleSetDataAsync()) {
+ return () -> setLargeIcon(largeIcon);
+ }
+
+ mLargeIcon = largeIcon;
+ return () -> {};
+ }
+
+ @RemotableViewMethod(asyncImpl = "setLargeIconAsync")
public void setLargeIcon(Icon largeIcon) {
mLargeIcon = largeIcon;
}
@@ -133,16 +167,11 @@ public class CallLayout extends FrameLayout {
mConversationIconView.setImageIcon(icon);
}
-
- public void setSetDataAsyncEnabled(boolean setDataAsyncEnabled) {
- mSetDataAsyncEnabled = setDataAsyncEnabled;
- }
-
/**
* Async implementation for setData
*/
public Runnable setDataAsync(Bundle extras) {
- if (!mSetDataAsyncEnabled) {
+ if (!Flags.callStyleSetDataAsync()) {
return () -> setData(extras);
}
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index f93b3068a229..07cbaadf0580 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "InputEventReceiver"
+#define ATRACE_TAG ATRACE_TAG_INPUT
//#define LOG_NDEBUG 0
@@ -46,6 +47,16 @@ static const char* toString(bool value) {
return value ? "true" : "false";
}
+/**
+ * Trace a bool variable, writing "1" if the value is "true" and "0" otherwise.
+ * TODO(b/311142655): delete this tracing. It's only useful for debugging very specific issues.
+ * @param var the name of the variable
+ * @param value the value of the variable
+ */
+static void traceBoolVariable(const char* var, bool value) {
+ ATRACE_INT(var, value ? 1 : 0);
+}
+
static struct {
jclass clazz;
@@ -130,6 +141,7 @@ NativeInputEventReceiver::NativeInputEventReceiver(
mMessageQueue(messageQueue),
mBatchedInputEventPending(false),
mFdEvents(0) {
+ traceBoolVariable("mBatchedInputEventPending", mBatchedInputEventPending);
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName().c_str());
}
@@ -311,6 +323,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
if (consumeBatches) {
mBatchedInputEventPending = false;
+ traceBoolVariable("mBatchedInputEventPending", mBatchedInputEventPending);
}
if (outConsumedBatch) {
*outConsumedBatch = false;
@@ -344,6 +357,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
}
mBatchedInputEventPending = true;
+ traceBoolVariable("mBatchedInputEventPending", mBatchedInputEventPending);
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
getInputChannelName().c_str());
@@ -355,6 +369,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching batched input events.");
mBatchedInputEventPending = false; // try again later
+ traceBoolVariable("mBatchedInputEventPending", mBatchedInputEventPending);
}
}
return OK;
@@ -371,15 +386,15 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
}
}
- jobject inputEventObj;
+ ScopedLocalRef<jobject> inputEventObj(env);
switch (inputEvent->getType()) {
case InputEventType::KEY:
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Received key event.", getInputChannelName().c_str());
}
inputEventObj =
- android_view_KeyEvent_fromNative(env,
- static_cast<KeyEvent&>(*inputEvent));
+ android_view_KeyEvent_obtainAsCopy(env,
+ static_cast<KeyEvent&>(*inputEvent));
break;
case InputEventType::MOTION: {
@@ -447,20 +462,19 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
default:
assert(false); // InputConsumer should prevent this from ever happening
- inputEventObj = nullptr;
}
- if (inputEventObj) {
+ if (inputEventObj.get()) {
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());
}
env->CallVoidMethod(receiverObj.get(),
- gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
+ gInputEventReceiverClassInfo.dispatchInputEvent, seq,
+ inputEventObj.get());
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching input event.");
skipCallbacks = true;
}
- env->DeleteLocalRef(inputEventObj);
} else {
ALOGW("channel '%s' ~ Failed to obtain event object.",
getInputChannelName().c_str());
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index b5fbb22215b4..88b02baab924 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -359,7 +359,7 @@ static jboolean nativeSendKeyEvent(JNIEnv* env, jclass clazz, jlong senderPtr,
jint seq, jobject eventObj) {
sp<NativeInputEventSender> sender =
reinterpret_cast<NativeInputEventSender*>(senderPtr);
- const KeyEvent event = android_view_KeyEvent_toNative(env, eventObj);
+ const KeyEvent event = android_view_KeyEvent_obtainAsCopy(env, eventObj);
status_t status = sender->sendKeyEvent(seq, &event);
return !status;
}
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index a0d081d2cd26..50d2cbe2ce74 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -221,7 +221,7 @@ static jlong nativeSendKeyEvent(JNIEnv* env, jobject clazz, jlong ptr, jobject e
jboolean predispatch) {
InputQueue* queue = reinterpret_cast<InputQueue*>(ptr);
KeyEvent* event = queue->createKeyEvent();
- *event = android_view_KeyEvent_toNative(env, eventObj);
+ *event = android_view_KeyEvent_obtainAsCopy(env, eventObj);
if (predispatch) {
event->setFlags(event->getFlags() | AKEY_EVENT_FLAG_PREDISPATCH);
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index a79e37afd4cd..2b19ddfed5eb 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -219,10 +219,10 @@ static jobjectArray nativeGetEvents(JNIEnv *env, jobject clazz, jlong ptr,
result = env->NewObjectArray(jsize(events.size()), gKeyEventClassInfo.clazz, NULL);
if (result) {
for (size_t i = 0; i < events.size(); i++) {
- jobject keyEventObj = android_view_KeyEvent_fromNative(env, events.itemAt(i));
- if (!keyEventObj) break; // threw OOM exception
- env->SetObjectArrayElement(result, jsize(i), keyEventObj);
- env->DeleteLocalRef(keyEventObj);
+ ScopedLocalRef<jobject> keyEventObj =
+ android_view_KeyEvent_obtainAsCopy(env, events.itemAt(i));
+ if (!keyEventObj.get()) break; // threw OOM exception
+ env->SetObjectArrayElement(result, jsize(i), keyEventObj.get());
}
}
}
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index a9c991919361..ca8752f93e11 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -94,26 +94,28 @@ static struct {
// ----------------------------------------------------------------------------
-jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent& event) {
+ScopedLocalRef<jobject> android_view_KeyEvent_obtainAsCopy(JNIEnv* env, const KeyEvent& event) {
ScopedLocalRef<jbyteArray> hmac = toJbyteArray(env, event.getHmac());
- jobject eventObj =
- env->CallStaticObjectMethod(gKeyEventClassInfo.clazz, gKeyEventClassInfo.obtain,
- event.getId(), event.getDownTime(), event.getEventTime(),
- event.getAction(), event.getKeyCode(),
- event.getRepeatCount(), event.getMetaState(),
- event.getDeviceId(), event.getScanCode(), event.getFlags(),
- event.getSource(), event.getDisplayId(), hmac.get(),
- nullptr);
+ ScopedLocalRef<jobject>
+ eventObj(env,
+ env->CallStaticObjectMethod(gKeyEventClassInfo.clazz,
+ gKeyEventClassInfo.obtain, event.getId(),
+ event.getDownTime(), event.getEventTime(),
+ event.getAction(), event.getKeyCode(),
+ event.getRepeatCount(), event.getMetaState(),
+ event.getDeviceId(), event.getScanCode(),
+ event.getFlags(), event.getSource(),
+ event.getDisplayId(), hmac.get(), nullptr));
if (env->ExceptionCheck()) {
ALOGE("An exception occurred while obtaining a key event.");
LOGE_EX(env);
env->ExceptionClear();
- return NULL;
+ return ScopedLocalRef<jobject>(env);
}
return eventObj;
}
-KeyEvent android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj) {
+KeyEvent android_view_KeyEvent_obtainAsCopy(JNIEnv* env, jobject eventObj) {
jint id = env->GetIntField(eventObj, gKeyEventClassInfo.mId);
jint deviceId = env->GetIntField(eventObj, gKeyEventClassInfo.mDeviceId);
jint source = env->GetIntField(eventObj, gKeyEventClassInfo.mSource);
diff --git a/core/jni/android_view_KeyEvent.h b/core/jni/android_view_KeyEvent.h
index bc4876a7a835..838f0130bd40 100644
--- a/core/jni/android_view_KeyEvent.h
+++ b/core/jni/android_view_KeyEvent.h
@@ -17,21 +17,24 @@
#ifndef _ANDROID_VIEW_KEYEVENT_H
#define _ANDROID_VIEW_KEYEVENT_H
-#include "jni.h"
+#include <nativehelper/scoped_local_ref.h>
#include <utils/Errors.h>
#include <utils/threads.h>
+#include "jni.h"
+
namespace android {
class KeyEvent;
/* Obtains an instance of a DVM KeyEvent object as a copy of a native KeyEvent instance.
* Returns NULL on error. */
-extern jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent& event);
+extern ScopedLocalRef<jobject> android_view_KeyEvent_obtainAsCopy(JNIEnv* env,
+ const KeyEvent& event);
/* Copies the contents of a DVM KeyEvent object to a native KeyEvent instance.
* Returns non-zero on error. */
-extern KeyEvent android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj);
+extern KeyEvent android_view_KeyEvent_obtainAsCopy(JNIEnv* env, jobject eventObj);
/* Recycles a DVM KeyEvent object.
* Key events should only be recycled if they are owned by the system since user
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 2e9f1790a4a5..285dee364bb0 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -77,39 +77,28 @@ MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj
env->GetLongField(eventObj, gMotionEventClassInfo.mNativePtr));
}
-static void android_view_MotionEvent_setNativePtr(JNIEnv* env, jobject eventObj,
- MotionEvent* event) {
- env->SetLongField(eventObj, gMotionEventClassInfo.mNativePtr,
- reinterpret_cast<jlong>(event));
+static void android_view_MotionEvent_setNativePtr(JNIEnv* env, ScopedLocalRef<jobject>& eventObj,
+ MotionEvent* event) {
+ env->SetLongField(eventObj.get(), gMotionEventClassInfo.mNativePtr,
+ reinterpret_cast<jlong>(event));
}
-jobject android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent& event) {
- jobject eventObj = env->CallStaticObjectMethod(gMotionEventClassInfo.clazz,
- gMotionEventClassInfo.obtain);
- if (env->ExceptionCheck() || !eventObj) {
- ALOGE("An exception occurred while obtaining a motion event.");
- LOGE_EX(env);
- env->ExceptionClear();
- return NULL;
- }
-
- MotionEvent* destEvent = android_view_MotionEvent_getNativePtr(env, eventObj);
- if (!destEvent) {
- destEvent = new MotionEvent();
- android_view_MotionEvent_setNativePtr(env, eventObj, destEvent);
- }
-
+ScopedLocalRef<jobject> android_view_MotionEvent_obtainAsCopy(JNIEnv* env,
+ const MotionEvent& event) {
+ std::unique_ptr<MotionEvent> destEvent = std::make_unique<MotionEvent>();
destEvent->copyFrom(&event, true);
- return eventObj;
+ return android_view_MotionEvent_obtainFromNative(env, std::move(destEvent));
}
-jobject android_view_MotionEvent_obtainFromNative(JNIEnv* env, std::unique_ptr<MotionEvent> event) {
+ScopedLocalRef<jobject> android_view_MotionEvent_obtainFromNative(
+ JNIEnv* env, std::unique_ptr<MotionEvent> event) {
if (event == nullptr) {
- return nullptr;
+ return ScopedLocalRef<jobject>(env);
}
- jobject eventObj =
- env->CallStaticObjectMethod(gMotionEventClassInfo.clazz, gMotionEventClassInfo.obtain);
- if (env->ExceptionCheck() || !eventObj) {
+ ScopedLocalRef<jobject> eventObj(env,
+ env->CallStaticObjectMethod(gMotionEventClassInfo.clazz,
+ gMotionEventClassInfo.obtain));
+ if (env->ExceptionCheck() || !eventObj.get()) {
LOGE_EX(env);
LOG_ALWAYS_FATAL("An exception occurred while obtaining a Java motion event.");
}
diff --git a/core/jni/android_view_MotionEvent.h b/core/jni/android_view_MotionEvent.h
index e81213608d68..b1bf1c435e26 100644
--- a/core/jni/android_view_MotionEvent.h
+++ b/core/jni/android_view_MotionEvent.h
@@ -17,21 +17,24 @@
#ifndef _ANDROID_VIEW_MOTIONEVENT_H
#define _ANDROID_VIEW_MOTIONEVENT_H
-#include "jni.h"
+#include <nativehelper/scoped_local_ref.h>
#include <utils/Errors.h>
+#include "jni.h"
+
namespace android {
class MotionEvent;
/* Obtains an instance of a DVM MotionEvent object as a copy of a native MotionEvent instance.
* Returns NULL on error. */
-extern jobject android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent& event);
+extern ScopedLocalRef<jobject> android_view_MotionEvent_obtainAsCopy(JNIEnv* env,
+ const MotionEvent& event);
/* Obtains an instance of a Java MotionEvent object, taking over the ownership of the provided
* native MotionEvent instance. Crashes on error. */
-extern jobject android_view_MotionEvent_obtainFromNative(JNIEnv* env,
- std::unique_ptr<MotionEvent> event);
+extern ScopedLocalRef<jobject> android_view_MotionEvent_obtainFromNative(
+ JNIEnv* env, std::unique_ptr<MotionEvent> event);
/* Gets the underlying native MotionEvent instance within a DVM MotionEvent object.
* Returns NULL if the event is NULL or if it is uninitialized. */
diff --git a/core/jni/android_view_MotionPredictor.cpp b/core/jni/android_view_MotionPredictor.cpp
index de3e81c7088b..0707e99205aa 100644
--- a/core/jni/android_view_MotionPredictor.cpp
+++ b/core/jni/android_view_MotionPredictor.cpp
@@ -61,7 +61,8 @@ static jobject android_view_MotionPredictor_nativePredict(JNIEnv* env, jclass cl
MotionPredictor* predictor = reinterpret_cast<MotionPredictor*>(ptr);
return android_view_MotionEvent_obtainFromNative(env,
predictor->predict(static_cast<nsecs_t>(
- predictionTimeNanos)));
+ predictionTimeNanos)))
+ .release();
}
static jboolean android_view_MotionPredictor_nativeIsPredictionAvailable(JNIEnv* env, jclass clazz,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 710b5f8a5a6a..8720f947bb8d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8370,6 +8370,12 @@
android:process=":ui">
</activity>
+ <activity android:name="com.android.internal.app.SetScreenLockDialogActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true"
+ android:process=":ui">
+ </activity>
+
<activity android:name="com.android.internal.app.BlockedAppActivity"
android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index da67efee4842..6cd2c4e277ea 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Vingerafdruk is gestaaf"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Gesig is gestaaf"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gesig is gestaaf; druk asseblief bevestig"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Vingerafdrukhardeware is nie beskikbaar nie"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Kan nie vingerafdruk opstel nie"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Vingerafdrukopstelling het uitgetel. Probeer weer."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Vingerafdrukhandeling is gekanselleer"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Vingerafdrukhandeling is deur gebruiker gekanselleer"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Te veel pogings. Gebruik eerder skermslot."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Te veel pogings. Gebruik eerder skermslot."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Kan nie vingerafdruk verwerk nie. Probeer weer."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Geen vingerafdrukke is geregistreer nie"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Hierdie toetstel het nie \'n vingerafdruksensor nie"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor is tydelik gedeaktiveer"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Kan nie vingerafdruksensor gebruik nie. Besoek ’n verskaffer wat herstelwerk doen."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Aan/af-skakelaar is gedruk"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gebruik vingerafdruk"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 4a7f6f3b7976..b485b4bdd5f0 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"የጣት አሻራ ትክክለኛነት ተረጋግጧል"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ፊት ተረጋግጧል"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ፊት ተረጋግጧል፣ እባክዎ አረጋግጥን ይጫኑ"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"የጣት አሻራ ሃርድዌር አይገኝም"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"የጣት አሻራን ማዋቀር አልተቻለም"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"የጣት አሻራ ውቅረት ጊዜው አብቅቷል። እንደገና ይሞክሩ።"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"የጣት አሻራ ክወና ተሰርዟል"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"የጣት አሻራ ክወና በተጠቃሚ ተሰርዟል"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"በጣም ብዙ ሙከራዎች። በምትኩ የማያ ገፅ መቆለፊያን ይጠቀሙ።"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"በጣም ብዙ ሙከራዎች። በምትኩ የማያ ገፅ መቆለፊያን ይጠቀሙ።"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"የጣት አሻራን ማሰናዳት አልተቻለም። እንደገና ይሞክሩ።"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ምንም የጣት አሻራዎች አልተመዘገቡም"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ይህ መሣሪያ የጣት አሻራ ዳሳሽ የለውም"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"ዳሳሽ ለጊዜው ተሰናክሏል"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"የጣት አሻራ ዳሳሽን መጠቀም አይቻልም። የጥገና አገልግሎት አቅራቢን ይጎብኙ።"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"የኃይል አዝራር ተጭኗል"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ጣት <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"የጣት አሻራ ይጠቀሙ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index b767c5704cef..b12c1154f038 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -670,24 +670,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"تم مصادقة بصمة الإصبع"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"تمّت مصادقة الوجه"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"تمّت مصادقة الوجه، يُرجى الضغط على \"تأكيد\"."</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"جهاز التعرّف على بصمة الإصبع غير متاح"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"يتعذّر إعداد بصمة الإصبع."</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"انتهت مهلة إعداد بصمة الإصبع. يُرجى إعادة المحاولة."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"تم إلغاء عملية قراءة بصمة الإصبع"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ألغى المستخدم عملية قراءة بصمة الإصبع"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"تم إجراء عدد كبير جدًا من المحاولات. عليك استخدام قفل الشاشة بدلاً من ذلك."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"تم إجراء عدد كبير جدًا من المحاولات. عليك استخدام قفل الشاشة بدلاً من ذلك."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"تتعذّر معالجة بصمة الإصبع. يُرجى إعادة المحاولة."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ما مِن بصمات إصبع مسجَّلة"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"لا يحتوي هذا الجهاز على جهاز استشعار بصمات الأصابع."</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"تم مؤقتًا إيقاف أداة الاستشعار"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"يتعذّر استخدام أداة استشعار بصمة الإصبع. يُرجى التواصل مع مقدِّم خدمات إصلاح."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"تم الضغط على زر التشغيل"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"الإصبع <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"استخدام بصمة الإصبع"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 19d8357644d8..01705ad6cd68 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ফিংগাৰপ্ৰিণ্টৰ সত্যাপন কৰা হ’ল"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল, অনুগ্ৰহ কৰি ‘নিশ্চিত কৰক’ বুটামটো টিপক"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ফিংগাৰপ্ৰিণ্ট হাৰ্ডৱেৰ নাই"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ফিংগাৰপ্ৰিণ্ট ছেট আপ কৰিব নোৱাৰি"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ফিংগাৰপ্ৰিণ্ট ছেটআপ কৰাৰ সময় উকলি গৈছে। পুনৰ চেষ্টা কৰক।"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ফিংগাৰপ্ৰিণ্টৰ দ্বাৰা বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কাৰ্যটো বাতিল কৰা হৈছে"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ব্যৱহাৰকাৰীয়ে ফিংগাৰপ্ৰিণ্টৰ দ্বাৰা বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কাৰ্য বাতিল কৰিছে"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"অতি বেছিসংখ্যক প্ৰয়াস। ইয়াৰ সলনি স্ক্ৰীন লক ব্যৱহাৰ কৰক।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"অতি বেছিসংখ্যক প্ৰয়াস। ইয়াৰ সলনি স্ক্ৰীন লক ব্যৱহাৰ কৰক।"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ফিংগাৰপ্ৰিণ্ট চিনাক্তকৰণ প্ৰক্ৰিয়া কৰিব পৰা নাই। পুনৰ চেষ্টা কৰক।"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"কোনো ফিংগাৰপ্ৰিণ্ট পঞ্জীয়ন কৰা হোৱা নাই"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"ছেন্সৰটো সাময়িকভাৱে অক্ষম হৈ আছে"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰ ব্যৱহাৰ কৰিব নোৱাৰি। মেৰামতি সেৱা প্ৰদানকাৰী কোনো প্ৰতিষ্ঠানলৈ যাওক।"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"পাৱাৰ বুটাম টিপা হৈছে"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> আঙুলি"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index b82ffdced01a..4ed5f5eadd5b 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Barmaq izi doğrulandı"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Üz doğrulandı"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Üz təsdiq edildi, təsdiq düyməsinə basın"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Barmaq izi avadanlığı əlçatan deyil"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Barmaq izini ayarlamaq mümkün deyil"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Barmaq izi ayarlama vaxtı bitib. Yenidən cəhd edin."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Barmaq izi əməliyyatı ləğv edildi"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"İstifadəçi barmaq izi əməliyyatını ləğv etdi"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Həddindən çox cəhd edilib. Əvəzində ekran kilidindən istifadə edin."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Həddindən çox cəhd edilib. Əvəzində ekran kilidindən istifadə edin."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Barmaq izini emal etmək mümkün deyil. Yenidən cəhd edin."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Barmaq izi qeydiyyatdan keçirilməyib"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Bu cihazda barmaq izi sensoru yoxdur"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor müvəqqəti deaktivdir"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Barmaq izi sensorundan istifadə etmək olmur. Servis mərkəzinə gedin."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Qidalanma düyməsi basılıb"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Barmaq <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Barmaq izini istifadə edin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 655d1d81efe3..b31dca411edb 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisak prsta je potvrđen"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je potvrđeno"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je potvrđeno. Pritisnite Potvrdi"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardver za otisak prsta nije dostupan"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Podešavanje otiska prsta nije uspelo"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Vreme za podešavanje otiska prsta je isteklo. Probajte ponovo."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Radnja sa otiskom prsta je otkazana"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Korisnik je otkazao radnju sa otiskom prsta"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Previše pokušaja. Koristite zaključavanje ekrana umesto toga."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Koristite zaključavanje ekrana umesto toga."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Obrađivanje otiska prsta nije uspelo. Probajte ponovo."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nije registrovan nijedan otisak prsta"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ovaj uređaj nema senzor za otisak prsta"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Senzor je privremeno onemogućen"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Ne možete da koristite senzor za otisak prsta. Posetite servis."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Pritisnuto je dugme za uključivanje"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Koristite otisak prsta"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index d36c7d1b8b66..2bd78e9080c1 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -668,24 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Адбітак пальца распазнаны"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Твар распазнаны"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Твар распазнаны. Націсніце, каб пацвердзіць"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Сканер адбіткаў пальцаў недаступны"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не ўдалося захаваць адбітак пальца"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Наладжванне адбітка пальца не завершана. Паўтарыце спробу."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Аперацыя з адбіткам пальца скасавана"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Аперацыя з адбіткам пальца скасавана карыстальнікам"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Занадта шмат спроб. Скарыстайце блакіроўку экрана."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Занадта шмат спроб. Скарыстайце блакіроўку экрана."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не ўдалося апрацаваць адбітак пальца. Паўтарыце спробу."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Адбіткі пальцаў не зарэгістраваны"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"На гэтай прыладзе няма сканера адбіткаў пальцаў"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Сканер часова адключаны"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Не ўдалося скарыстаць сканер адбіткаў пальцаў. Звярніцеся ў сэрвісны цэнтр."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Націснута кнопка сілкавання"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Палец <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Выкарыстоўваць адбітак пальца"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 24d5d293d6f3..dc1e92ca7037 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечатъкът е удостоверен"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицето е удостоверено"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицето е удостоверено. Моля, натиснете „Потвърждаване“"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Хардуерът за отпечатъци не е налице"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не може да се настрои отпечатък"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Настройването на отпечатък не завърши навреме. Опитайте отново."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Операцията за отпечатък е анулирана"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Операцията за удостоверяване чрез отпечатък бе анулирана от потребителя"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Твърде много опити. Вместо това използвайте опция за заключване на екрана."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Твърде много опити. Вместо това използвайте опция за заключване на екрана."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Отпечатъкът не може да бъде обработен. Опитайте отново."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Няма регистрирани отпечатъци"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Това устройство няма сензор за отпечатъци"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Сензорът е временно деактивиран"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Сензорът за отпечатъци не може да се използва. Посетете оторизиран сервиз."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Бутонът за захранване е натиснат"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Пръст <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Използване на отпечатък"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index f7f63cc4cc6c..5dbaad626ab9 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"আঙ্গুলের ছাপ যাচাই করা হয়েছে"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ফেস যাচাই করা হয়েছে"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ফেস যাচাই করা হয়েছে, \'কনফার্ম করুন\' বোতাম প্রেস করুন"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ফিঙ্গারপ্রিন্ট হার্ডওয়্যার উপলভ্য নেই"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"আঙ্গুলের ছাপ সেট-আপ করতে পারছি না"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ফিঙ্গারপ্রিন্ট সেট-আপ করার সময় সীমা পেরিয়ে গেছে। আবার চেষ্টা করুন।"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ফিঙ্গারপ্রিন্ট অপারেশন বাতিল করা হয়েছে"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ব্যবহারকারী ফিঙ্গারপ্রিন্ট অপারেশন বাতিল করেছেন"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"অনেকবার চেষ্টা করেছেন। পরিবর্তে স্ক্রিন লক ব্যবহার করুন।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"অনেকবার চেষ্টা করেছেন। পরিবর্তে স্ক্রিন লক ব্যবহার করুন।"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ফিঙ্গারপ্রিন্ট প্রসেস করা যায়নি। আবার চেষ্টা করুন।"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"কোনও ফিঙ্গারপ্রিন্ট নথিভুক্ত করা নেই"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"এই ডিভাইসে আঙ্গুলের ছাপের সেন্সর নেই"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"সেন্সর অস্থায়ীভাবে বন্ধ আছে"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ফিঙ্গারপ্রিন্ট সেন্সর ব্যবহার করা যাচ্ছে না। একজন মেরামতি মিস্ত্রির কাছে যান।"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"পাওয়ার বোতাম প্রেস করা হয়েছে"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"আঙ্গুল <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"আঙ্গুলের ছাপ ব্যবহার করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index b433f7762dd1..838a2db90408 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisak prsta je potvrđen"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je provjereno"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je provjereno, pritisnite dugme za potvrdu"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardver za prepoznavanje otiska prsta nije dostupan"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nije moguće postaviti otisak prsta"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Postavljanje otiska prsta je isteklo. Pokušajte ponovo."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Radnja s otiskom prsta je otkazana"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Korisnik je otkazao radnju s otiskom prsta"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Previše pokušaja. Umjesto toga koristite zaključavanje ekrana."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Umjesto toga koristite zaključavanje ekrana."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nije moguće obraditi otisak prsta. Pokušajte ponovo."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nije registriran nijedan otisak prsta"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ovaj uređaj nema senzor za otisak prsta"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Senzor je privremeno onemogućen"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Nije moguće koristiti senzor za otisak prsta. Posjetite pružaoca usluga za popravke."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Dugme za uključivanje je pritisnuto"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Koristi otisak prsta"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index f93d6bb9562f..feb67c42ccf6 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"L\'empremta digital s\'ha autenticat"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Cara autenticada"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Cara autenticada; prem el botó per confirmar"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"El maquinari d\'empremtes digitals no està disponible"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"No es pot configurar l\'empremta digital"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Temps d\'espera esgotat per configurar l\'empremta digital. Torna-ho a provar."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"S\'ha cancel·lat l\'operació d\'empremta digital"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"L\'usuari ha cancel·lat l\'operació d\'empremta digital"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Massa intents. Utilitza el bloqueig de pantalla."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Massa intents. Utilitza el bloqueig de pantalla."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"No es pot processar l\'empremta digital. Torna-ho a provar."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"No s\'ha registrat cap empremta digital"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Aquest dispositiu no té sensor d\'empremtes digitals"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"El sensor està desactivat temporalment"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"No es pot utilitzar el sensor d\'empremtes digitals. Visita un proveïdor de reparacions."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"S\'ha premut el botó d\'engegada"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dit <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilitza l\'empremta digital"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 7edc6b46c623..1c0529a71e59 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -668,24 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisk byl ověřen"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Obličej byl ověřen"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Obličej byl ověřen, stiskněte tlačítko pro potvrzení"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Není k dispozici hardware ke snímání otisků prstů"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Otisk prstu se nepodařilo nastavit"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Časový limit nastavení otisku prstu vypršel. Zkuste to znovu."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operace otisku prstu byla zrušena"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Uživatel operaci s otiskem prstu zrušil"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Příliš mnoho pokusů. Použijte zámek obrazovky."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Příliš mnoho pokusů. Použijte zámek obrazovky."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Otisk prstu nelze zpracovat. Zkuste to znovu."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nejsou zaregistrovány žádné otisky prstů"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Toto zařízení nemá snímač otisků prstů"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Senzor je dočasně deaktivován"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Snímač otisků prstů nelze použít. Navštivte servis"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Bylo stisknut vypínač"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Použít otisk prstu"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index b5938fd09d96..3010dc7ec883 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeraftrykket blev godkendt"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansigtet er godkendt"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansigtet er godkendt. Tryk på Bekræft."</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardware til aflæsning af fingeraftryk er ikke tilgængelig"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Fingeraftrykket kan ikke gemmes"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Konfigurationen af fingeraftryk fik timeout. Prøv igen."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingeraftrykshandlingen er annulleret"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingeraftrykshandlingen er annulleret af brugeren"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Du har brugt for mange forsøg. Brug skærmlåsen i stedet."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Du har brugt for mange forsøg. Brug skærmlåsen i stedet."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Fingeraftrykket kan ikke behandles. Prøv igen."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Der er ikke registreret et fingeraftryk"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Denne enhed har ingen fingeraftrykslæser"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensoren er midlertidigt deaktiveret"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Fingeraftrykssensoren kan ikke bruges. Få den repareret."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Der blev trykket på afbryderknappen"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Fingeraftryk <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Brug fingeraftryk"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 59c7d4a0d389..803b96720a01 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerabdruck wurde authentifiziert"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Gesicht authentifiziert"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gesicht authentifiziert, bitte bestätigen"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Fingerabdruck-Hardware nicht verfügbar"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Fingerabdruck konnte nicht eingerichtet werden"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Zeitüberschreitung bei Fingerabdruckeinrichtung. Versuch es noch einmal."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingerabdruckvorgang abgebrochen"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingerabdruckvorgang vom Nutzer abgebrochen"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Zu viele Versuche. Verwende stattdessen die Displaysperre."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Zu viele Versuche. Verwende stattdessen die Displaysperre."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Fingerabdruck kann nicht verarbeitet werden. Versuch es noch einmal."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Keine Fingerabdrücke registriert"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Dieses Gerät hat keinen Fingerabdrucksensor"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Der Sensor ist vorübergehend deaktiviert"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Fingerabdrucksensor kann nicht verwendet werden. Suche einen Reparaturdienstleister auf."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Ein-/Aus-Taste wurde gedrückt"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Fingerabdruck verwenden"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 762bd52bed0c..c030d25ecada 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Η ταυτότητα του δακτυλικού αποτυπώματος ελέγχθηκε"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Έγινε έλεγχος ταυτότητας προσώπου"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Έγινε έλεγχος ταυτότητας προσώπου, πατήστε \"Επιβεβαίωση\""</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Ο εξοπλισμός δακτυλικού αποτυπώματος δεν είναι διαθέσιμος"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Δεν είναι δυνατή η ρύθμιση του δακτυλικού αποτυπώματος"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Λήξη χρονικού ορίου ρύθμισης δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε από τον χρήστη"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Υπερβολικά πολλές προσπάθειες. Χρησιμοποιήστε εναλλακτικά το κλείδωμα οθόνης."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Υπερβολικά πολλές προσπάθειες. Χρησιμοποιήστε εναλλακτικά το κλείδωμα οθόνης."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Δεν είναι δυνατή η επεξεργασία του δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Δεν έχουν καταχωριστεί δακτυλικά αποτυπώματα"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικού αποτυπώματος"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Ο αισθητήρας απενεργοποιήθηκε προσωρινά"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Δεν είναι δυνατή η χρήση του αισθητήρα δακτυλικών αποτυπωμάτων. Επισκεφτείτε έναν πάροχο υπηρεσιών επισκευής."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Το κουμπί λειτουργίας πατήθηκε"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Δάχτυλο <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Χρήση δακτυλικού αποτυπώματος"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 719c4232a52d..cecdce61f3e2 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Fingerprint hardware not available"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingerprint operation cancelled"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingerprint operation cancelled by user"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"No fingerprints enrolled"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"This device does not have a fingerprint sensor"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor temporarily disabled"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Can\'t use fingerprint sensor. Visit a repair provider."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index e317067c53f9..c3cf40402b42 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated, please press confirm"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Fingerprint hardware not available"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingerprint operation canceled"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingerprint operation canceled by user"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"No fingerprints enrolled"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"This device does not have a fingerprint sensor"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor temporarily disabled"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Can’t use fingerprint sensor. Visit a repair provider."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 0e58b1d17615..cda0168b4600 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Fingerprint hardware not available"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingerprint operation cancelled"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingerprint operation cancelled by user"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"No fingerprints enrolled"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"This device does not have a fingerprint sensor"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor temporarily disabled"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Can\'t use fingerprint sensor. Visit a repair provider."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 3c6b6712b84b..f979e270448c 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Fingerprint hardware not available"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingerprint operation cancelled"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingerprint operation cancelled by user"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"No fingerprints enrolled"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"This device does not have a fingerprint sensor"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor temporarily disabled"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Can\'t use fingerprint sensor. Visit a repair provider."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 34a020f4395a..e620c35f89ad 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‎‏‏‏‎‎‎Fingerprint authenticated‎‏‎‎‏‎"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‏‎‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‏‎‎‎‏‏‎‏‎‎‎‎‎‎‏‏‏‎Face authenticated‎‏‎‎‏‎"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‏‏‎‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎Face authenticated, please press confirm‎‏‎‎‏‎"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‎‏‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‎‎‏‎Fingerprint hardware not available‎‏‎‎‏‎"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‏‎‎‎Can’t set up fingerprint‎‏‎‎‏‎"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‎‏‏‏‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‎Fingerprint setup timed out. Try again.‎‏‎‎‏‎"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‎‏‎‎‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎Fingerprint operation canceled‎‏‎‎‏‎"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‎‎‎‏‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‎‏‎‎‏‎‏‏‎‎‏‏‎‏‏‏‏‎Fingerprint operation canceled by user‎‏‎‎‏‎"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‎Too many attempts. Use screen lock instead.‎‏‎‎‏‎"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‎‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‎‏‏‎Too many attempts. Use screen lock instead.‎‏‎‎‏‎"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎Can’t process fingerprint. Try again.‎‏‎‎‏‎"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‎‎No fingerprints enrolled‎‏‎‎‏‎"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‎‏‏‏‎This device does not have a fingerprint sensor‎‏‎‎‏‎"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‏‎‎‎‏‎‎‏‎‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎Sensor temporarily disabled‎‏‎‎‏‎"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‏‏‏‎‏‎‎‎‎Can’t use fingerprint sensor. Visit a repair provider.‎‏‎‎‏‎"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‎‎‎‎‎‏‏‎‎Power button pressed‎‏‎‎‏‎"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‏‎‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‏‎‎‏‎‎Finger ‎‏‎‎‏‏‎<xliff:g id="FINGERID">%d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‏‎‏‎‏‏‎‏‎‏‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‎Use fingerprint‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 7fb0d1f446d1..da500d7d50eb 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Se autenticó la huella dactilar"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Se autenticó el rostro"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Se autenticó el rostro; presiona Confirmar"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"El hardware de huella dactilar no está disponible"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"No se puede configurar la huella dactilar"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Se agotó el tiempo de espera para configurar la huella dactilar. Vuelve a intentarlo."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Se canceló la operación de huella dactilar"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"El usuario canceló la operación de huella dactilar"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Demasiados intentos. Utiliza el bloqueo de pantalla en su lugar."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Utiliza el bloqueo de pantalla en su lugar."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"No se puede procesar la huella dactilar. Vuelve a intentarlo."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"No se inscribieron huellas dactilares"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo no tiene sensor de huellas dactilares"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Se inhabilitó temporalmente el sensor"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"No se puede usar el sensor de huellas dactilares. Consulta a un proveedor de reparaciones."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Se presionó el botón de encendido"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar huella dactilar"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 7d0c4e0318f4..35cd1ebe784f 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Se ha autenticado la huella digital"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Cara autenticada"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Se ha autenticado la cara, pulsa para confirmar"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Lector de huellas digitales no disponible"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"No se puede configurar la huella digital"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Tiempo de espera para configurar la huella digital agotado. Inténtalo de nuevo."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operación de huella digital cancelada"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"El usuario ha cancelado la operación de huella digital"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Demasiados intentos. Usa el bloqueo de pantalla."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Usa el bloqueo de pantalla."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"No se puede procesar la huella digital. Inténtalo de nuevo."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"No se ha registrado ninguna huella digital"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"El dispositivo no tiene ningún sensor de huellas digitales"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor inhabilitado en estos momentos"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"No se puede usar el sensor de huellas digitales. Visita un proveedor de reparaciones."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Se ha pulsado el botón de encendido"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar huella digital"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 250341f07a82..24ee88437930 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Sõrmejälg autenditi"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Nägu on autenditud"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Nägu on autenditud, vajutage käsku Kinnita"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Sõrmejälje riistvara pole saadaval"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Sõrmejälge ei saa seadistada"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Sõrmejälje seadistamine aegus. Proovige uuesti."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Sõrmejälje toiming tühistati"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Kasutaja tühistas sõrmejälje kasutamise"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Liiga palju katseid. Kasutage selle asemel ekraanilukku."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Liiga palju katseid. Kasutage selle asemel ekraanilukku."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Sõrmejälge ei õnnestu töödelda. Proovige uuesti."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Ühtegi sõrmejälge pole registreeritud"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Selles seadmes pole sõrmejäljeandurit"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Andur on ajutiselt keelatud"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Sõrmejäljeandurit ei saa kasutada. Külastage remonditeenuse pakkujat."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Vajutati toitenuppu"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Sõrmejälg <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Sõrmejälje kasutamine"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 3c5377989497..583f8a995d43 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Autentifikatu da hatz-marka"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Autentifikatu da aurpegia"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Autentifikatu da aurpegia; sakatu Berretsi"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hatz-marken hardwarea ez dago erabilgarri"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Ezin da konfiguratu hatz-marka"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Hatz-marka konfiguratzeko denbora-muga gainditu da. Saiatu berriro."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Bertan behera utzi da hatz-marka bidezko eragiketa"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Erabiltzaileak bertan behera utzi du hatz-marka bidezko eragiketa"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Saiakera gehiegi egin dira. Erabili pantailaren blokeoa."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Saiakera gehiegi egin dira. Erabili pantailaren blokeoa."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Ezin da prozesatu hatz-marka. Saiatu berriro."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Ez dago hatz-markarik erregistratuta"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Gailu honek ez du hatz-marken sentsorerik"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sentsorea aldi baterako desgaitu da"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Ezin da erabili hatz-marken sentsorea. Joan konponketak egiten dituen hornitzaile baten webgunera edo dendara."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Etengailua sakatu da"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. hatza"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Erabili hatz-marka"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 640f93f73563..52de6dac5f28 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"اثر انگشت اصالت‌سنجی شد"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چهره اصالت‌سنجی شد"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چهره اصالت‌سنجی شد، لطفاً تأیید را فشار دهید"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"سخت‌افزار اثر انگشت دردسترس نیست"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"اثر انگشت راه‌اندازی نشد"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"مهلت تنظیم اثر انگشت به‌پایان رسید. دوباره امتحان کنید."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"عملکرد اثر انگشت لغو شد"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"کاربر عملیات اثر انگشت را لغو کرد"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"تلاش‌ها از حد مجاز بیشتر شده است. به‌جای آن از قفل صفحه استفاده کنید."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"تلاش‌های بیش‌ازحد. حالا از قفل صفحه استفاده کنید."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"اثر انگشت پردازش نشد. دوباره امتحان کنید."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"اثر انگشتی ثبت نشده است"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"این دستگاه حسگر اثر انگشت ندارد"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"حسگر موقتاً غیرفعال شده است"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"نمی‌توان از حسگر اثر انگشت استفاده کرد. به ارائه‌دهنده خدمات تعمیر مراجعه کنید."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"دکمه روشن/خاموش فشار داده شد"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"انگشت <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"استفاده از اثر انگشت"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 18b9ee2a5a64..20d4c14c8c86 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Sormenjälki tunnistettu"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Kasvot tunnistettu"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Kasvot tunnistettu, valitse Vahvista"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Sormenjälkilaitteisto ei käytettävissä"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Sormenjälkeä ei voi ottaa käyttöön"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Sormenjäljen käyttöönotto aikakatkaistu. Yritä uudelleen."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Sormenjälkitoiminto peruttu"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Käyttäjä on perunut sormenjälkitoiminnon"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Liian monta yritystä. Käytä näytön lukituksen avaustapaa."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Liian monta yritystä. Käytä näytön lukituksen avaustapaa."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Sormenjälkeä ei voida käsitellä. Yritä uudelleen."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Sormenjälkiä ei ole lisätty"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Laitteessa ei ole sormenjälkitunnistinta."</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Tunnistin väliaikaisesti pois käytöstä"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Sormenjälkitunnistinta ei voi käyttää. Ota yhteys korjauspalveluun."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Virtapainiketta on painettu"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Sormi <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Käytä sormenjälkeä"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 1eb9eb21c842..b6349427b89c 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Visage authentifié"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Visage authentifié, veuillez appuyer sur le bouton Confirmer"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Lecteur d\'empreintes digitales non accessible"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Impossible de configurer l\'empreinte digitale"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Délai dépassé pour configurer l\'empreinte digitale. Réessayez."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Opération d\'empreinte digitale annulée"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Opération d\'authentification par empreinte digitale annulée par l\'utilisateur"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Empreinte digitale non traitable. Réessayez."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Aucune empreinte digitale enregistrée"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Cet appareil ne possède pas de capteur d\'empreintes digitales"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Capteur désactivé temporairement"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Impossible d\'utiliser le capteur d\'empreintes digitales. Visitez un fournisseur de services de réparation."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Vous avez appuyé sur l\'interrupteur"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c349b1cf1774..f0ed3d6290b5 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -201,7 +201,7 @@
<string name="device_ownership_relinquished" msgid="4080886992183195724">"L\'administrateur a mis l\'appareil à disposition pour un usage personnel"</string>
<string name="network_logging_notification_title" msgid="554983187553845004">"L\'appareil est géré"</string>
<string name="network_logging_notification_text" msgid="1327373071132562512">"Votre organisation gère cet appareil et peut surveiller le trafic réseau. Appuyez ici pour obtenir plus d\'informations."</string>
- <string name="location_changed_notification_title" msgid="3620158742816699316">"Des application peuvent accéder à votre position"</string>
+ <string name="location_changed_notification_title" msgid="3620158742816699316">"Des applications peuvent accéder à votre position"</string>
<string name="location_changed_notification_text" msgid="7158423339982706912">"Contactez votre administrateur pour en savoir plus"</string>
<string name="geofencing_service" msgid="3826902410740315456">"Service de géorepérage"</string>
<string name="country_detector" msgid="7023275114706088854">"Détecteur de pays"</string>
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Visage authentifié"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Visage authentifié, veuillez appuyer sur \"Confirmer\""</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Lecteur d\'empreintes digitales indisponible"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Impossible de configurer l\'empreinte"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Délai de configuration de l\'empreinte dépassé. Réessayez."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Opération d\'authentification par empreinte digitale annulée"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Opération d\'authentification par empreinte digitale annulée par l\'utilisateur"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Impossible de reconnaître l\'empreinte digitale. Réessayez."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Aucune empreinte digitale enregistrée"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Aucun lecteur d\'empreinte digitale n\'est installé sur cet appareil"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Lecteur d\'empreintes temporairement désactivé"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Impossible d\'utiliser le lecteur d\'empreintes digitales. Contactez un réparateur."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Bouton Marche/Arrêt appuyé"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string>
@@ -802,8 +796,8 @@
<string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"Permet à l\'application de s\'associer à l\'interface de niveau supérieur d\'un service de fournisseur de conditions. Ne devrait pas être nécessaire pour les applications standards."</string>
<string name="permlab_bindDreamService" msgid="4776175992848982706">"associer à un service d\'écran de veille interactif"</string>
<string name="permdesc_bindDreamService" msgid="9129615743300572973">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un service d\'écran de veille interactif. Cette autorisation ne devrait jamais être nécessaire pour les applications standards."</string>
- <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"faire appel à l\'application de configuration fournie par l\'opérateur"</string>
- <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"Permet à l\'application autorisée de faire appel à l\'application de configuration fournie par l\'opérateur. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string>
+ <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"invoquer l\'appli de configuration fournie par l\'opérateur"</string>
+ <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"Permet au titulaire d\'invoquer l\'appli de configuration fournie par l\'opérateur. Ne devrait pas être nécessaire pour les applis standards."</string>
<string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"détecter des observations sur les conditions du réseau"</string>
<string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"Permet à une application de détecter des observations sur les conditions du réseau. Les applications standards ne devraient pas nécessiter cette autorisation."</string>
<string name="permlab_setInputCalibration" msgid="932069700285223434">"modifier le calibrage du périphérique d\'entrée"</string>
@@ -839,7 +833,7 @@
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Contrôle le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouille votre appareil Android TV ou en efface toutes les données si le nombre maximal de mots de passe incorrects autorisé est dépassé."</string>
<string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller le système d\'infoloisirs ou effacer toutes ses données si le nombre maximal de tentatives de saisie du mot de passe est atteint."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller le téléphone ou effacer toutes ses données si le nombre maximal de tentatives de saisie du mot de passe est atteint"</string>
- <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Contrôlez le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouillez la tablette ou effacez toutes les informations sur l\'utilisateur si le nombre maximal de mots de passe incorrects autorisés est dépassé."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Contrôlez le nombre de mots de passe incorrects saisis pour déverrouiller l\'écran, et verrouillez la tablette ou effacez toutes les données de cet utilisateur si trop de mots de passe incorrects sont saisis."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Contrôle le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouille votre appareil Android TV ou efface toutes les données de cet utilisateur si le nombre maximal de mots de passe incorrects autorisé est dépassé."</string>
<string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller le système d\'infoloisirs ou effacer toutes les données de ce profil si le nombre maximal de tentatives de saisie du mot de passe est atteint."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Contrôlez le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouillez le téléphone ou effacez toutes les informations sur l\'utilisateur si le nombre maximal de mots de passe incorrects autorisés est dépassé."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 2cc80d5cf9a7..da791af2ec3a 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Autenticouse a impresión dixital"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Autenticouse a cara"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Autenticouse a cara, preme Confirmar"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"O hardware de impresión dixital non está dispoñible"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Non se puido configurar a impresión dixital"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Esgotouse o tempo para configurar a impresión dixital. Téntao de novo."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Cancelouse a operación de impresión dixital"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"O usuario cancelou a operación de impresión dixital"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Houbo demasiados intentos. Mellor usa o bloqueo de pantalla."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Mellor usa o bloqueo de pantalla."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Non se pode procesar a impresión dixital Téntao de novo."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Non se rexistrou ningunha impresión dixital"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo non ten sensor de impresión dixital"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"O sensor está desactivado temporalmente"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Non se pode usar o sensor de impresión dixital. Vai a un servizo de reparación."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Premeuse o botón de acendido"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilizar impresión dixital"</string>
@@ -1003,7 +997,7 @@
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Téntao de novo"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Desbloquea para gozar todas as funcións e datos"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"Superouse o número máximo de intentos de desbloqueo facial"</string>
- <string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"Non hai ningunha SIM"</string>
+ <string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"Non hai SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"Non hai ningunha SIM na tableta."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="3903140876952198273">"Non hai ningunha SIM no dispositivo Android TV."</string>
<string name="lockscreen_missing_sim_message" product="default" msgid="6184187634180854181">"Non hai ningunha SIM no teléfono."</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 1ef8c4703205..8f6affa7a91e 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ફિંગરપ્રિન્ટ પ્રમાણિત કરી"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ચહેરા પ્રમાણિત"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ચહેરા પ્રમાણિત, કૃપા કરીને કન્ફર્મ કરો"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ફિંગરપ્રિન્ટ હાર્ડવેર ઉપલબ્ધ નથી"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ફિંગરપ્રિન્ટનું સેટઅપ કરી શકતા નથી"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ફિંગરપ્રિન્ટનું સેટઅપ કરવાનો સમય સમાપ્ત થઈ ગયો. ફરી પ્રયાસ કરો."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ફિંગરપ્રિન્ટ ઓપરેશન રદ કર્યું"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"વપરાશકર્તાએ ફિંગરપ્રિન્ટ ચકાસવાની પ્રક્રિયા રદ કરી"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ઘણા બધા પ્રયાસો. તેને બદલે સ્ક્રીન લૉકનો ઉપયોગ કરો."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ઘણા બધા પ્રયાસો. વિકલ્પ તરીકે સ્ક્રીન લૉકનો ઉપયોગ કરો."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ફિંગરપ્રિન્ટની પ્રક્રિયા કરી શકતા નથી. ફરી પ્રયાસ કરો."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"કોઈ ફિંગરપ્રિન્ટની નોંધણી કરવામાં આવી નથી"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"આ ડિવાઇસમાં કોઈ ફિંગરપ્રિન્ટ સેન્સર નથી"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"સેન્સર હંગામી રીતે બંધ કર્યું છે"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ફિંગરપ્રિન્ટ સેન્સરનો ઉપયોગ કરી શકાતો નથી. રિપેર કરવાની સેવા આપતા પ્રદાતાની મુલાકાત લો."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"પાવર બટન દબાવવામાં આવ્યું"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"આંગળી <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index bd49843657a7..53b39ffed09b 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"फ़िंगरप्रिंट की पुष्टि हो गई"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"चेहरे की पहचान की गई"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"चेहरे की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"फ़िंगरप्रिंट को पहचानने वाला हार्डवेयर मौजूद नहीं है"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"फ़िंगरप्रिंट सेट अप नहीं किया जा सका"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"फ़िंगरप्रिंट सेटअप करने का समय खत्म हो गया. फिर से कोशिश करें."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"फिंगरप्रिंट की पुष्टि से जुड़ी कार्रवाई रद्द कर दी गई है"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"उपयोगकर्ता ने फिंगरप्रिंट की पुष्टि से जुड़ी कार्रवाई रद्द कर दी है"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"कई बार कोशिश की जा चुकी है. इसके बजाय, स्क्रीन लॉक का इस्तेमाल करें."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"इससे ज़्यादा बार कोशिश नहीं की जा सकती. इसके बजाय, स्क्रीन लॉक का इस्तेमाल करें."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"फ़िंगरप्रिंट की पहचान नहीं की जा सकी. फिर से कोशिश करें."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"कोई फ़िंगरप्रिंट रजिस्टर नहीं किया गया है"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"इस डिवाइस में फ़िंगरप्रिंट सेंसर नहीं है"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"सेंसर कुछ समय के लिए बंद कर दिया गया है"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"फ़िंगरप्रिंट सेंसर इस्तेमाल नहीं किया जा सकता. फ़िंगरप्रिंट सेंसर को रिपेयर करने की सेवा देने वाली कंपनी से संपर्क करें."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"पावर बटन दबाने की वजह से गड़बड़ी हुई"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"फ़िंगरप्रिंट <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फ़िंगरप्रिंट इस्तेमाल करें"</string>
@@ -1171,7 +1165,7 @@
<string name="Midnight" msgid="8176019203622191377">"मध्‍यरात्रि"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
- <string name="selectAll" msgid="1532369154488982046">"सभी को चुनें"</string>
+ <string name="selectAll" msgid="1532369154488982046">"पूरा टेक्स्ट चुनें"</string>
<string name="cut" msgid="2561199725874745819">"काटें"</string>
<string name="copy" msgid="5472512047143665218">"कॉपी करें"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"क्लिपबोर्ड पर कॉपी नहीं हो सका"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index f1e9bd6d0636..9af2496fadf1 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Autentificirano otiskom prsta"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je autentificirano"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je autentificirano, pritisnite Potvrdi"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardver za otisak prsta nije dostupan"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Postavljanje otiska prsta nije uspjelo"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Vrijeme za postavljanje otiska prsta je isteklo. Pokušajte ponovo."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Radnja otiska prsta je otkazana"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Radnju s otiskom prsta otkazao je korisnik"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Previše pokušaja. Umjesto toga upotrijebite zaključavanje zaslona."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Umjesto toga upotrijebite zaključavanje zaslona."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Obrada otiska prsta nije uspjela. Pokušajte ponovo."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nije registriran nijedan otisak prsta"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ovaj uređaj nema senzor otiska prsta"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Senzor je privremeno onemogućen"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Senzor otiska prsta ne može se koristiti. Posjetite servisera."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Pritisnuta je tipka za uključivanje/isključivanje"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Upotreba otiska prsta"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 26c607788ac1..17f80d12a1fe 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Ujjlenyomat hitelesítve"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Arc hitelesítve"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Arc hitelesítve; nyomja meg a Megerősítés lehetőséget"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Az ujjlenyomat-olvasó hardverhez nem lehet hozzáférni."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nem sikerült beállítani az ujjlenyomatot"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Lejárt az ujjlenyomat-beállítás időkorlátja. Próbálkozzon újra."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Ujjlenyomattal kapcsolatos művelet megszakítva."</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Az ujjlenyomattal kapcsolatos műveletet a felhasználó megszakította."</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Túl sokszor próbálkozott. Használja inkább a képernyőzárat."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Túl sok próbálkozás. Használja inkább a képernyőzárat."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nem sikerült feldolgozni az ujjlenyomatot. Próbálkozzon újra."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nincsenek regisztrált ujjlenyomatok."</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ez az eszköz nem rendelkezik ujjlenyomat-érzékelővel"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Az érzékelő átmenetileg le van tiltva."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Nem lehet használni az ujjlenyomat-érzékelőt. Keresse fel a szervizt."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Bekapcsológomb megnyomva"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. ujj"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Ujjlenyomat használata"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index e82dc708de73..51d0dedbef5a 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Մատնահետքը նույնականացվեց"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Դեմքը ճանաչվեց"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Դեմքը ճանաչվեց: Սեղմեք «Հաստատել»:"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Մատնահետքերի սկաներն անհասանելի է"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Հնարավոր չէ կարգավորել մատնահետքը"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Մատնահետքի կարգավորման ժամանակը սպառվել է։ Նորից փորձեք։"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Մատնահետքի օգտագործմամբ գործողությունը չեղարկվել է"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Մատնահետքի օգտագործմամբ գործողությունը չեղարկվել է օգտատիրոջ կողմից"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Չափազանց շատ փորձեր են արվել։ Օգտագործեք էկրանի կողպումը։"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Չափազանց շատ փորձեր են արվել։ Օգտագործեք էկրանի կողպումը։"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Չի հաջողվում մշակել մատնահետքը։ Նորից փորձեք։"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Գրանցված մատնահետքեր չկան"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Սարքը չունի մատնահետքի սկաներ"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Տվիչը ժամանակավորապես անջատված է"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Մատնահետքերի սկաները հնարավոր չէ օգտագործել։ Այցելեք սպասարկման կենտրոն։"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Սեղմվել է սնուցման կոճակը"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Մատնահետք <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Օգտագործել մատնահետք"</string>
@@ -1607,8 +1601,8 @@
<string name="storage_internal" msgid="8490227947584914460">"Ներքին ընդհանուր կրիչ"</string>
<string name="storage_sd_card" msgid="3404740277075331881">"SD քարտ"</string>
<string name="storage_sd_card_label" msgid="7526153141147470509">"SD քարտ <xliff:g id="MANUFACTURER">%s</xliff:g>-ից"</string>
- <string name="storage_usb_drive" msgid="448030813201444573">"USB սարքավար"</string>
- <string name="storage_usb_drive_label" msgid="6631740655876540521">"USB սարքավար <xliff:g id="MANUFACTURER">%s</xliff:g>-ից"</string>
+ <string name="storage_usb_drive" msgid="448030813201444573">"USB կրիչ"</string>
+ <string name="storage_usb_drive_label" msgid="6631740655876540521">"USB կրիչ <xliff:g id="MANUFACTURER">%s</xliff:g>-ից"</string>
<string name="storage_usb" msgid="2391213347883616886">"USB կրիչ"</string>
<string name="extract_edit_menu_button" msgid="63954536535863040">"Խմբագրել"</string>
<string name="data_usage_warning_title" msgid="9034893717078325845">"Զգուշացում թրաֆիկի օգտագործման մասին"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 52d1463c7e24..29dc27307f36 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Sidik jari diautentikasi"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Wajah diautentikasi"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Wajah diautentikasi, silakan tekan konfirmasi"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardware sidik jari tidak tersedia"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Tidak dapat menyiapkan sidik jari"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Waktu penyiapan sidik jari habis. Coba lagi."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operasi sidik jari dibatalkan"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Operasi sidik jari dibatalkan oleh pengguna"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Terlalu banyak upaya gagal. Gunakan kunci layar."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Terlalu banyak upaya gagal. Gunakan kunci layar."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Tidak dapat memproses sidik jari. Coba lagi."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Tidak ada sidik jari yang terdaftar"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Perangkat ini tidak memiliki sensor sidik jari"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor dinonaktifkan untuk sementara"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Tidak dapat menggunakan sensor sidik jari. Kunjungi penyedia reparasi."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Tombol daya ditekan"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gunakan sidik jari"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 81e5a62d5c38..ca702fa3e68c 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingrafar staðfest"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Andlit staðfest"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Andlit staðfest, ýttu til að staðfesta"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Fingrafarabúnaður er ekki til staðar"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Ekki er hægt að setja upp fingrafar"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingrafarsuppsetning rann út á tíma. Reyndu aftur."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Hætt var við að nota fingrafar"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Notandi hætti við að nota fingrafar"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Of margar tilraunir. Notaðu skjálás í staðinn."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Of margar tilraunir. Notaðu skjálás í staðinn."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Ekki tekst að vinna úr fingrafari. Reyndu aftur."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Engin fingraför hafa verið skráð"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Þetta tæki er ekki með fingrafaralesara"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Slökkt var á lesara tímabundið"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Ekki er hægt að nota fingrafaralesara. Þú verður að fara á verkstæði."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Ýtt á aflrofa"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Fingur <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Nota fingrafar"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index e5a8b2506366..2cb3d3d9826d 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Impronta autenticata"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Volto autenticato"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Volto autenticato, premi Conferma"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Lettore di impronte digitali non disponibile"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Impossibile configurare l\'impronta"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Timeout configurazione impronta. Riprova."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operazione associata all\'impronta annullata"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Operazione di autenticazione dell\'impronta annullata dall\'utente"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Troppi tentativi. Usa il blocco schermo."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Troppi tentativi. Usa il blocco schermo."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Impossibile elaborare l\'impronta. Riprova."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nessuna impronta registrata"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Questo dispositivo non è dotato di sensore di impronte"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensore temporaneamente disattivato"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Impossibile usare il sensore di impronte digitali. Contatta un fornitore di servizi di riparazione."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Tasto di accensione premuto"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dito <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usa l\'impronta"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 1e040c19bd89..3e7c79e050de 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"טביעת האצבע אומתה"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"זיהוי הפנים בוצע"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"זיהוי הפנים בוצע. יש ללחוץ על אישור"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"החומרה לזיהוי טביעות אצבע לא זמינה"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"לא ניתן להגדיר טביעת אצבע"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"הזמן שהוקצב להגדרה של טביעת האצבע פג. יש לנסות שוב."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"הפעולה של טביעת האצבע בוטלה"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"הפעולה של טביעת האצבע בוטלה על ידי המשתמש"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"בוצעו יותר מדי ניסיונות. יש להשתמש בנעילת המסך במקום זאת."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"בוצעו יותר מדי ניסיונות. יש להשתמש בנעילת המסך במקום זאת."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"לא ניתן לעבד את טביעת האצבע. יש לנסות שוב."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"לא נסרקו טביעות אצבע"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"במכשיר הזה אין חיישן טביעות אצבע"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"החיישן מושבת באופן זמני"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"לא ניתן להשתמש בחיישן טביעות האצבע. צריך ליצור קשר עם ספק תיקונים."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"לחצן ההפעלה נלחץ"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"אצבע <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"שימוש בטביעת אצבע"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index f4382c4e2505..576a495fbdb9 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"指紋認証を完了しました"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"顔を認証しました"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"顔を認証しました。[確認] を押してください"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"指紋認証ハードウェアは使用できません"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"指紋を設定できません"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋の設定がタイムアウトしました。もう一度お試しください。"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"指紋認証操作がキャンセルされました"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"指紋認証操作がユーザーによりキャンセルされました"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"指紋を処理できません。もう一度お試しください。"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"指紋が登録されていません"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"このデバイスには指紋認証センサーがありません"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"センサーが一時的に無効になっています"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"指紋認証センサーを使用できません。修理業者に調整を依頼してください。"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"電源ボタンが押されました"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"指紋 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"指紋の使用"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 35f885b913f3..571ae42169c5 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"თითის ანაბეჭდი ავტორიზებულია"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"სახე ავტორიზებულია"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"სახე ავტორიზებულია, დააჭირეთ დადასტურებას"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"თითის ანაბეჭდის ამომცნობი მოწყობილობა მიუწვდომელია"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"თითის ანაბეჭდის დაყენება ვერ ხერხდება"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"თითის ანაბეჭდის დაყენების დრო ამოიწურა. ცადეთ ხელახლა."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"თითის ანაბეჭდის ოპერაცია გაუქმდა"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"თითის ანაბეჭდის ოპერაცია გააუქმა მომხმარებელმა"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"მეტისმეტად ბევრი მცდელობა იყო. სანაცვლოდ გამოიყენეთ ეკრანის დაბლოკვა."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"მეტისმეტად ბევრი მცდელობა იყო. სანაცვლოდ გამოიყენეთ ეკრანის დაბლოკვა."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"თითის ანაბეჭდის დამუშავება შეუძლებელია. ცადეთ ხელახლა."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"თითის ანაბეჭდები არ არის რეგისტრირებული"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ამ მოწყობილობას არ აქვს თითის ანაბეჭდის სენსორი"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"სენსორი დროებით გათიშულია"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"თითის ანაბეჭდის სენსორის გამოყენება შეუძლებელია. ეწვიეთ შეკეთების სერვისის პროვაიდერს."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ჩართვის ღილაკზე დაეჭირა"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"თითი <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"გამოიყენეთ თითის ანაბეჭდი"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 1e7a73b7c57b..4f50140b34e6 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Саусақ ізі аутентификацияланды"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Бет танылды"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Бет танылды, \"Растау\" түймесін басыңыз"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Саусақ ізін тану жабдығы қолжетімді емес."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Саусақ ізін орнату мүмкін емес."</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Саусақ ізін реттеу уақыты өтіп кетті. Қайталап көріңіз."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Саусақ ізі операциясынан бас тартылды."</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Пайдаланушы саусақ ізі операциясынан бас тартты."</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Тым көп әрекет жасалды. Орнына экран құлпын пайдаланыңыз."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Тым көп әрекет жасалды. Орнына экран құлпын пайдаланыңыз."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Саусақ ізін өңдеу мүмкін емес. Қайталап көріңіз."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Ешқандай саусақ іздері тіркелмеді."</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Бұл құрылғыда саусақ ізін оқу сканері жоқ"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Датчик уақытша өшірулі."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Саусақ ізін оқу сканерін пайдалану мүмкін емес. Жөндеу қызметіне барыңыз."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Қуат түймесі басулы."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>-саусақ"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Саусақ ізін пайдалану"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 37a4f817fd13..8c33299df473 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"បាន​ផ្ទៀង​ផ្ទាត់​ស្នាម​ម្រាមដៃ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"បានផ្ទៀងផ្ទាត់​មុខ"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"បានផ្ទៀងផ្ទាត់​មុខ សូម​ចុច​បញ្ជាក់"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"មិនអាចប្រើ​ហាតវែរស្កេនស្នាមម្រាមដៃ​បានទេ"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"មិនអាចរៀបចំ​ស្នាមម្រាមដៃបានទេ"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"កា​ររៀបចំ​ស្នាមម្រាមដៃបានអស់ម៉ោង។ សូមព្យាយាមម្ដងទៀត។"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"បានបោះបង់ប្រតិបត្តិការស្នាមម្រាមដៃ"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ប្រតិបត្តិការ​ស្នាម​ម្រាម​ដៃ​ត្រូវ​បាន​បោះ​បង់​ដោយ​អ្នក​ប្រើប្រាស់"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ព្យាយាម​ច្រើនដងពេក។ សូមប្រើការចាក់សោ​អេក្រង់ជំនួសវិញ។"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ព្យាយាម​ច្រើនដងពេក។ សូមប្រើការចាក់សោ​អេក្រង់ជំនួសវិញ។"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"មិនអាចដំណើរការស្នាមម្រាមដៃបានទេ។ សូមព្យាយាមម្ដងទៀត។"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"មិន​មាន​ការ​ថតបញ្ចូល​ស្នាម​ម្រាមដៃទេ"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ឧបករណ៍នេះ​មិនមាន​ឧបករណ៍ចាប់​ស្នាមម្រាមដៃទេ"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"បានបិទ​សេនស័រ​ជាបណ្តោះអាសន្ន"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"មិនអាចប្រើ​សេនស័រចាប់ស្នាមម្រាមដៃបានទេ។ សូមទាក់ទងក្រុមហ៊ុន​ផ្ដល់ការជួសជុល។"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"បាន​ចុច​ប៊ូតុង​ថាមពល"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ម្រាមដៃទី <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ប្រើស្នាមម្រាមដៃ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index db4bf180704a..e106f1692a1f 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಪ್ರಮಾಣೀಕರಣ ಮಾಡಲಾಗಿದೆ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ, ದೃಢೀಕರಣವನ್ನು ಒತ್ತಿ"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಹಾರ್ಡ್‌ವೇರ್ ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಸೆಟಪ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆಟಪ್ ಮಾಡುವ ಅವಧಿ ಮುಗಿದಿದೆ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ಬಳಕೆದಾರರು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಗೊಳಿಸಿದ್ದಾರೆ"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ಬದಲಾಗಿ ಸ್ಕ್ರೀನ್‌ಲಾಕ್ ಬಳಸಿ."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ಬದಲಾಗಿ ಪರದೆಲಾಕ್ ಬಳಸಿ."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ಯಾವುದೇ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‌ಗಳನ್ನು ನೋಂದಣಿ ಮಾಡಿಲ್ಲ"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ಈ ಸಾಧನವು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‌ ಸೆನ್ಸರ್ ಅನ್ನು ಹೊಂದಿಲ್ಲ"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"ಸೆನ್ಸರ್ ಅನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಬಳಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ರಿಪೇರಿ ಮಾಡುವವರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ಪವರ್ ಬಟನ್ ಒತ್ತಲಾಗಿದೆ"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ಫಿಂಗರ್ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಬಳಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index e654eb2030f0..ff480c5428b4 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"지문이 인증됨"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"얼굴이 인증되었습니다"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"얼굴이 인증되었습니다. 확인을 누르세요"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"지문 인식 하드웨어를 사용할 수 없습니다."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"지문을 설정할 수 없음"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"지문 설정 시간이 초과되었습니다. 다시 시도해 주세요."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"지문 인식 작업이 취소되었습니다."</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"사용자가 지문 인식 작업을 취소했습니다."</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"시도 횟수가 너무 많습니다. 화면 잠금을 대신 사용하세요."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"시도 횟수가 너무 많습니다. 화면 잠금을 대신 사용하세요."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"지문을 처리할 수 없습니다. 다시 시도해 주세요."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"등록된 지문이 없습니다."</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"기기에 지문 센서가 없습니다."</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"센서가 일시적으로 사용 중지되었습니다."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"지문 센서를 사용할 수 없습니다. 수리업체를 방문하세요."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"전원 버튼을 눌렀습니다."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"손가락 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"지문 사용"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 39ecc0260d9c..e44689d839cc 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Манжа изи текшерилди"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Жүздүн аныктыгы текшерилди"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Жүздүн аныктыгы текшерилди, эми \"Ырастоону\" басыңыз"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Манжа издеринин сканери жеткиликтүү эмес"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Манжа изи жөндөлбөй жатат"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Манжа изин коюу убакыты бүтүп калды. Кайра аракет кылыңыз."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Манжа изи менен аныктыгын текшерүү жокко чыгарылды"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Манжа изи менен аныктыгын текшерүүнү колдонуучу жокко чыгарды"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Өтө көп жолу аракет кылдыңыз. Экранды кулпулоо функциясын колдонуңуз."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Өтө көп жолу аракет кылдыңыз. Экранды кулпулоо функциясын колдонуңуз."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Манжа изи иштетилген жок. Кайра аракет кылыңыз."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Бир да манжа изи катталган эмес"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Бул түзмөктө манжа изинин сенсору жок"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Сенсор убактылуу өчүрүлгөн"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Манжа изинин сенсорун колдонууга болбойт. Тейлөө кызматына кайрылыңыз."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Кубат баскычы басылды"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>-манжа"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Манжа изин колдонуу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 48d62d614976..1f6c1a1594a9 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ພິສູດຢືນຢັນລາຍນິ້ວມືແລ້ວ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ພິສູດຢືນຢັນໃບໜ້າແລ້ວ"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ພິສູດຢືນຢັນໃບໜ້າແລ້ວ, ກະລຸນາກົດຢືນຢັນ"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ຮາດແວລາຍນິ້ວມືບໍ່ມີໃຫ້ໃຊ້"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ບໍ່ສາມາດຕັ້ງຄ່າລາຍນິ້ວມືໄດ້"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ໝົດເວລາຕັ້ງຄ່າລາຍນິ້ວມື. ກະລຸນາລອງໃໝ່."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ຍົກເລີກການເຮັດວຽກຂອງລາຍນິ້ວມືແລ້ວ"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ຜູ້ໃຊ້ໄດ້ຍົກເລີກການເຮັດວຽກຂອງລາຍນິ້ວມືແລ້ວ"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ພະຍາຍາມຫຼາຍເທື່ອເກີນໄປ. ກະລຸນາໃຊ້ການລອກໜ້າຈໍແທນ."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ພະຍາຍາມຫຼາຍເທື່ອເກີນໄປ. ກະລຸນາໃຊ້ການລອກໜ້າຈໍແທນ."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ບໍ່ສາມາດປະມວນຜົນລາຍນິ້ວມືໄດ້. ກະລຸນາລອງໃໝ່."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ບໍ່ມີການລົງທະບຽນລາຍນິ້ວມື"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີລາຍນິ້ວມື"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"ປິດການເຮັດວຽກຂອງເຊັນເຊີໄວ້ຊົ່ຄາວແລ້ວ"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ບໍ່ສາມາດໃຊ້ເຊັນ​ເຊີລາຍນິ້ວ​ມືໄດ້. ກະລຸນາໄປຫາຜູ້ໃຫ້ບໍລິການສ້ອມແປງ."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ກົດປຸ່ມເປີດປິດແລ້ວ"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ນີ້ວ​ມື <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ໃຊ້ລາຍນິ້ວມື"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 38aa52a17b3e..0196351bacb4 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -668,24 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Piršto antspaudas autentifikuotas"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Veidas autentifikuotas"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Veidas autentifikuotas, paspauskite patvirtinimo mygtuką"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Piršto antspaudo aparatinė įranga nepasiekiama"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nepavyko nustatyti kontrolinio kodo"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Baigėsi piršto atspaudo sąrankos skirtasis laikas. Bandykite dar kartą."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Piršto antspaudo operacija atšaukta"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Piršto antspaudo operaciją atšaukė naudotojas"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Per daug bandymų. Naudokite ekrano užraktą."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Per daug bandymų. Naudokite ekrano užraktą."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nepavyko apdoroti kontrolinio kodo. Bandykite dar kartą."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Neužregistruota jokių pirštų atspaudų"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Šiame įrenginyje nėra piršto antspaudo jutiklio"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Jutiklis laikinai išjungtas"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Negalima naudoti piršto atspaudo jutiklio. Apsilankykite pas taisymo paslaugos teikėją."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Paspaustas maitinimo mygtukas"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> pirštas"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Naudoti kontrolinį kodą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 165eccde23a4..46aae3f62458 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Pirksta nospiedums tika autentificēts."</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Seja autentificēta"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Seja ir autentificēta. Nospiediet pogu Apstiprināt."</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Pirksta nospieduma aparatūra nav pieejama."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nevar iestatīt pirksta nospiedumu"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Iestatot pirksta nospiedumu, iestājās noildze. Mēģiniet vēlreiz."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Nospieduma darbība neizdevās."</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Lietotājs atcēla pirksta nospieduma darbību."</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Pārāk daudz mēģinājumu. Izmantojiet ekrāna bloķēšanu."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Pārāk daudz mēģinājumu. Izmantojiet ekrāna bloķēšanu."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nevar apstrādāt pirksta nospiedumu. Mēģiniet vēlreiz."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nav reģistrēts neviens pirksta nospiedums."</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Šajā ierīcē nav pirksta nospieduma sensora."</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensors ir īslaicīgi atspējots."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Nevar izmantot pirksta nospieduma sensoru. Sazinieties ar remonta pakalpojumu sniedzēju."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Tika nospiesta barošanas poga"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. pirksts"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Pirksta nospieduma izmantošana"</string>
@@ -1302,12 +1296,12 @@
<string name="new_app_action" msgid="547772182913269801">"Atvērt <xliff:g id="NEW_APP">%1$s</xliff:g>"</string>
<string name="new_app_description" msgid="1958903080400806644">"Lietotne <xliff:g id="OLD_APP">%1$s</xliff:g> tiks aizvērta, neko nesaglabājot"</string>
<string name="dump_heap_notification" msgid="5316644945404825032">"Process <xliff:g id="PROC">%1$s</xliff:g> pārsniedza atmiņas ierobežojumu."</string>
- <string name="dump_heap_ready_notification" msgid="2302452262927390268">"<xliff:g id="PROC">%1$s</xliff:g> kaudzes izraksts ir gatavs"</string>
+ <string name="dump_heap_ready_notification" msgid="2302452262927390268">"<xliff:g id="PROC">%1$s</xliff:g> grēdas izraksts ir gatavs"</string>
<string name="dump_heap_notification_detail" msgid="8431586843001054050">"Apkopots kaudzes izraksts. Pieskarieties, lai kopīgotu."</string>
- <string name="dump_heap_title" msgid="4367128917229233901">"Vai kopīgot kaudzes izrakstu?"</string>
- <string name="dump_heap_text" msgid="1692649033835719336">"Process <xliff:g id="PROC">%1$s</xliff:g> pārsniedza atmiņas ierobežojumu (<xliff:g id="SIZE">%2$s</xliff:g>). Tika vākts kaudzes izraksts, ko varat kopīgot ar procesa izstrādātāju. Ņemiet vērā: kaudzes izrakstā var būt ietverta jūsu personas informācija, kurai var piekļūt lietojumprogramma."</string>
+ <string name="dump_heap_title" msgid="4367128917229233901">"Vai kopīgot grēdas izrakstu?"</string>
+ <string name="dump_heap_text" msgid="1692649033835719336">"Process <xliff:g id="PROC">%1$s</xliff:g> pārsniedza atmiņas ierobežojumu (<xliff:g id="SIZE">%2$s</xliff:g>). Tika vākts grēdas izraksts, ko varat kopīgot ar procesa izstrādātāju. Ņemiet vērā: grēdas izrakstā var būt ietverta jūsu personas informācija, kurai var piekļūt lietojumprogramma."</string>
<string name="dump_heap_system_text" msgid="6805155514925350849">"Process <xliff:g id="PROC">%1$s</xliff:g> pārsniedza atmiņas ierobežojumu (<xliff:g id="SIZE">%2$s</xliff:g>). Tika vākts kaudzes izraksts, ko varat kopīgot. Ievērojiet piesardzību, jo kaudzes izrakstā var būt ietverta visa sensitīvā personas informācija, kurai var piekļūt process, tostarp jūsu rakstīts teksts."</string>
- <string name="dump_heap_ready_text" msgid="5849618132123045516">"Ir pieejams procesa <xliff:g id="PROC">%1$s</xliff:g> kaudzes izraksts, ko varat kopīgot. Ievērojiet piesardzību, jo kaudzes izrakstā var būt ietverta visa sensitīvā personas informācija, kurai var piekļūt process, tostarp jūsu rakstīts teksts."</string>
+ <string name="dump_heap_ready_text" msgid="5849618132123045516">"Ir pieejams procesa <xliff:g id="PROC">%1$s</xliff:g> grēdas izraksts, ko varat kopīgot. Ievērojiet piesardzību, jo grēdas izrakstā var būt ietverta visa sensitīvā personas informācija, kurai var piekļūt process, tostarp jūsu rakstīts teksts."</string>
<string name="sendText" msgid="493003724401350724">"Izvēlieties darbību tekstam"</string>
<string name="volume_ringtone" msgid="134784084629229029">"Zvanītāja skaļums"</string>
<string name="volume_music" msgid="7727274216734955095">"Multivides skaļums"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index aa5645a30d4a..1c85e473fdb9 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечатокот е проверен"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицето е проверено"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицето е проверено, притиснете го копчето „Потврди“"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Не е достапен хардвер за отпечаток"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не може да се постави отпечаток"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Времето за поставување отпечаток истече. Обидете се повторно."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Автентикацијата со отпечаток е откажана"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Корисникот ја откажа автентикацијата со отпечаток"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Премногу обиди. Користете заклучување екран."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Премногу обиди. Користете заклучување екран."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не може да се обработи отпечатокот од прст. Обидете се повторно."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Не се регистрирани отпечатоци"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Уредов нема сензор за отпечатоци"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Сензорот е привремено оневозможен"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Не може да се користи сензорот за отпечатоци. Однесете го уредот на поправка."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Притиснато е копчето за вклучување"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Користи отпечаток"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 0ef12814e3c9..9917448c1e0d 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ഫിംഗർപ്രിന്റ് പരിശോധിച്ചുറപ്പിച്ചു"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു, സ്ഥിരീകരിക്കുക അമർത്തുക"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ഫിംഗർപ്രിന്റ് ഹാർഡ്‌വെയർ ലഭ്യമല്ല"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ഫിംഗർപ്രിന്റ് സജ്ജീകരിക്കാനാകില്ല"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ഫിംഗർപ്രിന്റ് സജ്ജീകരണം ടൈംഔട്ടായി. വീണ്ടും ശ്രമിക്കുക."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ഫിംഗർപ്രിന്റ് പ്രവർത്തനം റദ്ദാക്കി"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ഫിംഗർപ്രിന്റിന്റെ പ്രവർത്തനം ഉപയോക്താവ് റദ്ദാക്കി"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"നിരവധി ശ്രമങ്ങൾ. പകരം സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"നിരവധി ശ്രമങ്ങൾ. പകരം സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ഫിംഗർപ്രിന്റ് പ്രോസസ് ചെയ്യാനാകില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ഫിംഗർപ്രിന്റുകളൊന്നും എൻറോൾ ചെയ്‌തിട്ടില്ല"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ഈ ഉപകരണത്തിൽ ഫിംഗർപ്രിന്റ് സെൻസർ ഇല്ല"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"സെൻസർ താൽക്കാലികമായി പ്രവർത്തനരഹിതമാക്കി"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ഫിംഗർപ്രിന്റ് സെൻസർ ഉപയോഗിക്കാനാകില്ല. റിപ്പയർ കേന്ദ്രം സന്ദർശിക്കുക."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"പവർ ബട്ടൺ അമർത്തി"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ഫിംഗർ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index df4148ef296f..48d926ccf4e2 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Хурууны хээг нотолсон"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Царайг баталгаажууллаа"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Царайг баталгаажууллаа. Баталгаажуулах товчлуурыг дарна уу"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Хурууны хээ таних техник хангамж боломжгүй"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Хурууны хээ тохируулах боломжгүй"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Хурууны хээний тохируулга завсарласан. Дахин оролдоно уу."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Хурууны хээний үйл ажиллагааг цуцалсан"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Хурууны хээний үйл ажиллагааг хэрэглэгч цуцалсан"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Хэт олон удаа оролдлоо. Оронд нь дэлгэцийн түгжээ ашиглана уу."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Хэт олон удаа оролдлоо. Оронд нь дэлгэцийн түгжээ ашиглана уу."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Хурууны хээг боловсруулах боломжгүй. Дахин оролдоно уу."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Ямар ч хурууны хээ бүртгүүлээгүй"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Энэ төхөөрөмжид хурууны хээ мэдрэгч алга"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Мэдрэгчийг түр зуур идэвхгүй болгосон"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Хурууны хээ мэдрэгчийг ашиглах боломжгүй. Засварын үйлчилгээ үзүүлэгчид зочилно уу."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Асаах/Унтраах товчийг дарсан"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Хурууны хээ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Хурууны хээ ашиглах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 09c2ac3b2c48..8fb6b0f01960 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -453,7 +453,7 @@
<string name="permlab_getPackageSize" msgid="375391550792886641">"अ‍ॅप संचयन स्थान मोजा"</string>
<string name="permdesc_getPackageSize" msgid="742743530909966782">"अ‍ॅप ला त्याचा कोड, डेटा आणि कॅशे आकार पुनर्प्राप्त करण्यासाठी अनुमती देते"</string>
<string name="permlab_writeSettings" msgid="8057285063719277394">"सिस्टीम सेटिंग्ज सुधारित करा"</string>
- <string name="permdesc_writeSettings" msgid="8293047411196067188">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स आपल्या सिस्टीमचे कॉंफिगरेशन दूषित करू शकतात."</string>
+ <string name="permdesc_writeSettings" msgid="8293047411196067188">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अ‍ॅपला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स तुमच्या सिस्टीमचे कॉन्फिगरेशन दूषित करू शकतात."</string>
<string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"सुरूवातीस चालवा"</string>
<string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"जसे सिस्टम बूट करणे समाप्त करते तसे अ‍ॅप ला स्वतः सुरू करण्यास अनुमती देते. यामुळे टॅबलेट सुरू करण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर टॅबलेटला धीमे करण्यास अ‍ॅप ला अनुमती देते."</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"सिस्टम बूट होणे संपल्यावर ॲपला स्वतः सुरू होण्याची अनुमती देते. यामुळे तुमच्या Android TV डिव्हाइसला सुरू होण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर डिव्हाइसलाच धीमे करण्याची अनुमती ॲपला देते."</string>
@@ -582,9 +582,9 @@
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"फक्त तुमच्या Android TV डिव्हाइसलाच नाही, तर मल्टिकास्ट पत्ते वापरून एका वाय-फाय नेटवर्कवरील सर्व डिव्हाइसवर पाठविलेली पॅकेट प्राप्त करण्यासाठी ॲपला अनुमती देते. हे मल्टिकास्ट मोड नसताना वापरल्या जाणाऱ्या ऊर्जेपेक्षा अधिक उर्जा वापरते."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या फोनवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अ‍ॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
<string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"ब्लूटूथ सेटिंग्ज अ‍ॅक्सेस करा"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"स्थानिक ब्लूटूथ टॅबलेट कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अ‍ॅप ला अनुमती देते."</string>
- <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"तुमच्या Android TV डिव्हाइसवर ब्लूटूथ कॉंफिगर करण्याकरिता आणि पेअर केलेली आणि रीमोट डिव्हाइस शोधण्यासाठी ॲपला अनुमती देते."</string>
- <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"स्थानिक ब्लूटूथ फोन कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अ‍ॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"स्थानिक ब्लूटूथ टॅबलेट कॉन्फिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अ‍ॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"तुमच्या Android TV डिव्हाइसवर ब्लूटूथ कॉन्फिगर करण्याकरिता आणि पेअर केलेली आणि रीमोट डिव्हाइस शोधण्यासाठी ॲपला अनुमती देते."</string>
+ <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"स्थानिक ब्लूटूथ फोन कॉन्फिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_accessWimaxState" msgid="7029563339012437434">"WiMAX कनेक्ट करा आणि त्यावरून डिस्कनेक्ट करा"</string>
<string name="permdesc_accessWimaxState" msgid="5372734776802067708">"WiMAX सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या कोणत्याही WiMAX नेटवर्क विषयीची माहिती निर्धारित करण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_changeWimaxState" msgid="6223305780806267462">"WiMAX स्थिती बदला"</string>
@@ -592,9 +592,9 @@
<string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"ॲपला तुमच्या Android TV डिव्हाइसशी कनेक्ट करण्याची आणि तुमचे Android TV डिव्हाइस WiMAX नेटवर्कवरून डिस्कनेक्ट करण्याची परवानगी देते."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"WiMAX नेटवर्कवर फोन कनेक्ट करण्यास आणि त्यावरून फोन डिस्कनेक्ट करण्यास अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_bluetooth" msgid="586333280736937209">"ब्लूटूथ डीव्हाइससह जोडा"</string>
- <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"टॅबलेटवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अ‍ॅप ला अनुमती देते."</string>
- <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Android TV डिव्हाइसवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन तयार करण्यासाठी आणि स्वीकारण्यासाठी, ॲपला अनुमती देते."</string>
- <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"फोनवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अ‍ॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"टॅबलेटवर ब्लूटूथचे कॉन्फिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डिव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अ‍ॅपला अनुमती देते."</string>
+ <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Android TV डिव्हाइसवर ब्लूटूथ चे कॉन्फिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डिव्हाइससह कनेक्शन तयार करण्यासाठी आणि स्वीकारण्यासाठी, अ‍ॅपला अनुमती देते."</string>
+ <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"फोनवर ब्लूटूथचे कॉन्फिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डिव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अ‍ॅपला अनुमती देते."</string>
<string name="permlab_bluetooth_scan" msgid="5402587142833124594">"जवळपासची ब्लूटूथ डिव्‍हाइस शोधा आणि ती पेअर करा"</string>
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"ॲपला जवळपासची ब्लूटूथ डिव्‍हाइस शोधण्यासाठी आणि ती पेअर करण्यासाठी अनुमती देते"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"पेअर केलेल्या ब्लूटूथ डिव्‍हाइसशी कनेक्ट करा"</string>
@@ -665,25 +665,19 @@
<string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"चेहरा ओळखू शकत नाही. त्याऐवजी फिंगरप्रिंट वापरा."</string>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"फिंगरप्रिंट ऑथेंटिकेट केली आहे"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"चेहरा ऑथेंटिकेशन केलेला आहे"</string>
- <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"चेहरा ऑथेंटिकेशन केलेला आहे, कृपया कंफर्म प्रेस करा"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"चेहरा ऑथेंटिकेशन केलेला आहे, कृपया कन्फर्म प्रेस करा"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"फिंगरप्रिंट हार्डवेअर उपलब्‍ध नाही"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"फिंगरप्रिंट सेट करता आली नाही"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"फिंगरप्रिंट सेट करण्याची वेळ संपली आहे. पुन्हा प्रयत्न करा."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"फिंगरप्रिंट ऑपरेशन रद्द करण्यात आले आहे"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"वापरकर्त्याने फिंगरप्रिंट ऑपरेशन रद्द केले आहे"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"खूप जास्त प्रयत्न. त्याऐवजी स्क्रीन लॉक वापरा."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"खूप जास्त प्रयत्न. त्याऐवजी स्क्रीन लॉक वापरा."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"फिंगरप्रिंटवर प्रक्रिया करू शकत नाही. पुन्हा प्रयत्न करा."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"कोणत्याही फिंगरप्रिंटची नोंदणी करण्यात आली नाही"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"या डिव्हाइसवर फिंगरप्रिंट सेन्सर नाही"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"सेन्सर तात्पुरते बंद करण्यात आले आहे"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"फिंगरप्रिंट सेन्सर वापरू शकत नाही. दुरुस्तीच्या सेवा पुरवठादाराला भेट द्या."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"पॉवर बटण दाबले"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> बोट"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फिंगरप्रिंट वापरा"</string>
@@ -802,7 +796,7 @@
<string name="permlab_bindDreamService" msgid="4776175992848982706">"स्‍वप्न सेवेवर प्रतिबद्ध करा"</string>
<string name="permdesc_bindDreamService" msgid="9129615743300572973">"होल्‍डरला स्‍वप्नसेवेच्या शीर्ष-स्‍तराच्या इंटरफेसशी प्रतिबद्ध करण्‍यास अनुमती देते. सामान्‍य अ‍ॅप्‍सकरिता कधीही आवश्‍यक नसते."</string>
<string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"वाहकाद्वारे-प्रदान केलेल्‍या कॉन्‍फिगरेशन अ‍ॅपची विनंती करा"</string>
- <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"होल्‍डरला वाहकद्वारे-प्रदान केलेल्या कॉंफिगरेशन अ‍ॅपची विनंती करण्‍याची अनुमती देते. सामान्‍य अ‍ॅप्‍ससाठी कधीही आवश्‍यक नसावे."</string>
+ <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"होल्‍डरला वाहकाद्वारे दिलेल्या कॉन्फिगरेशन अ‍ॅपची विनंती करण्‍याची अनुमती देते. सामान्‍य अ‍ॅप्‍ससाठी कधीही आवश्‍यक नसावे."</string>
<string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"नेटवर्क स्‍थितींवरील निरीक्षणांसाठी ऐका"</string>
<string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"अनु्प्रयोगाला नेटवर्क स्‍थितींवरील निरीक्षणे ऐकण्‍यासाठी अनुमती देते. सामान्‍य अ‍ॅप्‍ससाठी कधीही आवश्‍यक नसावे."</string>
<string name="permlab_setInputCalibration" msgid="932069700285223434">"इनपुट डिव्हाइस कॅलिब्रेशन बदला"</string>
@@ -818,7 +812,7 @@
<string name="permlab_bindCarrierServices" msgid="2395596978626237474">"वाहक सेवांवर प्रतिबद्ध करा"</string>
<string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"वाहक सेवांवर प्रतिबद्ध करण्यासाठी होल्डरला अनुमती देते. सामान्य ॲप्ससाठी कधीही आवश्यकता नसावी."</string>
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"व्यत्यय आणू नका अ‍ॅक्सेस करा"</string>
- <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"व्यत्यय आणू नका कॉंफिगरेशन वाचण्यासाठी आणि लिहिण्यासाठी ॲपला अनुमती देते."</string>
+ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"व्यत्यय आणू नका हे कॉन्फिगरेशन वाचण्यासाठी आणि लिहिण्यासाठी अ‍ॅपला अनुमती देते."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"व्ह्यू परवानगी वापर सुरू करा"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"धारकास अ‍ॅपसाठी परवानगी वापरणे सुरू करण्याची अनुमती देते. सामान्य अ‍ॅप्ससाठी कधीही आवश्यकता नसते."</string>
<string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"परवानगीशी संबंधित निर्णय पाहणे सुरू करा"</string>
@@ -1425,8 +1419,8 @@
<string name="select_input_method" msgid="3971267998568587025">"इनपुट पद्धत निवडा"</string>
<string name="show_ime" msgid="6406112007347443383">"भौतिक कीबोर्ड सक्रिय असताना त्यास स्क्रीनवर ठेवा"</string>
<string name="hardware" msgid="3611039921284836033">"ऑन-स्क्रीन कीबोर्ड वापरा"</string>
- <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> कॉंफिगर करा"</string>
- <string name="select_multiple_keyboards_layout_notification_title" msgid="6999491025126641938">"वास्तविक कीबोर्ड कॉंफिगर करा"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> कॉन्फिगर करा"</string>
+ <string name="select_multiple_keyboards_layout_notification_title" msgid="6999491025126641938">"वास्तविक कीबोर्ड कॉन्फिगर करा"</string>
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"भाषा आणि लेआउट निवडण्यासाठी टॅप करा"</string>
<string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
@@ -2377,12 +2371,12 @@
<string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"बॅटरी सेव्हर सुरू असल्यामुळे Dual Screen उपलब्ध नाही. तुम्ही हे सेटिंग्ज मध्ये बंद करू शकता."</string>
<string name="device_state_notification_settings_button" msgid="691937505741872749">"सेटिंग्ज वर जा"</string>
<string name="device_state_notification_turn_off_button" msgid="6327161707661689232">"बंद करा"</string>
- <string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"<xliff:g id="DEVICE_NAME">%s</xliff:g> कॉंफिगर केले आहे"</string>
+ <string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"<xliff:g id="DEVICE_NAME">%s</xliff:g> कॉन्फिगर केले आहे"</string>
<string name="keyboard_layout_notification_one_selected_message" msgid="4314216053129257197">"कीबोर्ड लेआउट <xliff:g id="LAYOUT_1">%s</xliff:g> वर सेट केला. बदलण्यासाठी टॅप करा."</string>
<string name="keyboard_layout_notification_two_selected_message" msgid="1876349944065922950">"कीबोर्ड लेआउट <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g> वर सेट केला. बदलण्यासाठी टॅप करा."</string>
<string name="keyboard_layout_notification_three_selected_message" msgid="280734264593115419">"कीबोर्ड लेआउट <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> वर सेट केला. बदलण्यासाठी टॅप करा."</string>
<string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"कीबोर्ड लेआउट <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> वर सेट करा… बदलण्यासाठी टॅप करा."</string>
- <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"वास्तविक कीबोर्ड कॉंफिगर केला"</string>
+ <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"वास्तविक कीबोर्ड कॉन्फिगर केला"</string>
<string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"कीबोर्ड पाहण्यासाठी टॅप करा"</string>
<string name="profile_label_private" msgid="6463418670715290696">"खाजगी"</string>
<string name="profile_label_clone" msgid="769106052210954285">"क्लोन"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 5018aa8fddc7..7112ed389f99 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Cap jari disahkan"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Wajah disahkan"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Wajah disahkan, sila tekan sahkan"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Perkakasan cap jari tidak tersedia"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Tidak dapat menyediakan cap jari"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Persediaan cap jari telah tamat masa. Cuba lagi."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Pengendalian cap jari dibatalkan"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Pengendalian cap jari dibatalkan oleh pengguna"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Terlalu banyak percubaan. Gunakan kunci skrin."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Terlalu banyak percubaan. Gunakan kunci skrin."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Tidak dapat memproses cap jari. Cuba lagi."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Tiada cap jari didaftarkan"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Peranti ini tiada penderia cap jari"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Penderia dilumpuhkan untuk sementara"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Tidak dapat menggunakan penderia cap jari. Lawati penyedia pembaikan."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Butang kuasa ditekan"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gunakan cap jari"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 97121dd39f74..2c3e7b743afe 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"လက်ဗွေကို အထောက်အထား စိစစ်ပြီးပါပြီ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ၊ အတည်ပြုရန်ကို နှိပ်ပါ"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"လက်ဗွေစက် မရနိုင်ပါ"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"လက်ဗွေကို စနစ်ထည့်သွင်း၍ မရပါ"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"လက်ဗွေစနစ်ထည့်သွင်းချိန် ကုန်သွားပါပြီ။ ထပ်စမ်းကြည့်ပါ။"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"လက်ဗွေဖြင့် လုပ်ဆောင်ခြင်းကို ပယ်ဖျက်လိုက်သည်"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"လက်ဗွေဖြင့် လုပ်ဆောင်ခြင်းကို အသုံးပြုသူက ပယ်ဖျက်လိုက်သည်"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ကြိုးပမ်းမှုအကြိမ်ရေ များလွန်းသည်။ ဖန်သားပြင်လော့ခ်ချခြင်းကို အစားထိုးသုံးပါ။"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ကြိုးပမ်းမှုအကြိမ်ရေ များလွန်းသည်။ ဖန်သားပြင်လော့ခ်ချခြင်းကို အစားထိုးသုံးပါ။"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"လက်ဗွေကို လုပ်ဆောင်နိုင်ခြင်းမရှိပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"မည်သည့်လက်ဗွေကိုမျှ ထည့်သွင်းမထားပါ"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ဤစက်ပစ္စည်းတွင် လက်ဗွေအာရုံခံကိရိယာ မရှိပါ"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"အာရုံခံကိရိယာကို ယာယီပိတ်ထားသည်"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"လက်ဗွေ အာရုံခံကိရိယာကို သုံး၍မရပါ။ ပြုပြင်ရေး ဝန်ဆောင်မှုပေးသူထံသို့ သွားပါ။"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ဖွင့်ပိတ်ခလုတ် နှိပ်ထားသည်"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"လက်ချောင်း <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"လက်ဗွေ သုံးခြင်း"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index ca6df0e02195..541e1b540570 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrykket er godkjent"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansiktet er autentisert"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansiktet er autentisert. Trykk på Bekreft"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Maskinvare for fingeravtrykk er ikke tilgjengelig"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Kan ikke konfigurere fingeravtrykk"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Konfigureringen av fingeravtrykk er tidsavbrutt. Prøv på nytt."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingeravtrykk-operasjonen ble avbrutt"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingeravtrykk-operasjonen ble avbrutt av brukeren"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"For mange forsøk. Bruk skjermlås i stedet."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"For mange forsøk. Bruk skjermlås i stedet."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Kan ikke behandle fingeravtrykket. Prøv på nytt."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Ingen fingeravtrykk er registrert"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Denne enheten har ikke fingeravtrykkssensor"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensoren er midlertidig slått av"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Kan ikke bruke fingeravtrykkssensoren. Gå til en reparasjonsleverandør."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Av/på-knappen ble trykket"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Bruk fingeravtrykk"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index fd31ac4b6dbe..8c951e0a9dab 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"फिंगरप्रिन्ट प्रमाणीकरण गरियो"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"अनुहार प्रमाणीकरण गरियो"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"अनुहार प्रमाणीकरण गरियो, कृपया पुष्टि गर्नुहोस् थिच्नुहोस्"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"फिंगरप्रिन्ट हार्डवेयर उपलब्ध छैन"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"फिंगरप्रिन्ट सेटअप गर्न सकिएन"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"फिंगरप्रिन्ट सेट अप गर्ने समय सकियो। फेरि प्रयास गर्नुहोस्।"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"फिंगरप्रिन्टसम्बन्धी कारबाही रद्द गरियो"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"प्रयोगकर्ताले फिंगरप्रिन्टसम्बन्धी कारबाही रद्द गर्नुभयो"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"निकै धेरै पटक प्रयास गरिसकिएको छ। बरु स्क्रिन लक प्रयोग गर्नुहोस्।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"निकै धेरै पटक प्रयास गरिसकिएको छ। बरु स्क्रिन लक प्रयोग गर्नुहोस्।"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"फिंगरप्रिन्ट पहिचान गर्ने प्रक्रिया अघि बढाउन सकिएन। फेरि प्रयास गर्नुहोस्।"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"कुनै पनि फिंगरप्रिन्ट दर्ता गरिएको छैन"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"यो डिभाइसमा कुनै फिंगरप्रिन्ट सेन्सर छैन"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"केही समयका लागि सेन्सर अफ गरियो"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"फिंगरप्रिन्ट सेन्सर प्रयोग गर्न सकिएन। फिंगरप्रिन्ट सेन्सर मर्मत गर्ने सेवा प्रदायक कम्पनीमा सम्पर्क गर्नुहोस्।"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"पावर बटन थिचियो"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"औंला <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फिंगरप्रिन्ट प्रयोग गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index e68947e8ed42..bbcc2aa88440 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Vingerafdruk geverifieerd"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Gezicht geverifieerd"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gezicht geverifieerd. Druk op Bevestigen."</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardware voor vingerafdruk niet beschikbaar"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Kan vingerafdruk niet instellen"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Time-out bij instellen van vingerafdruk. Probeer het opnieuw."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Vingerafdrukbewerking geannuleerd"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Vingerafdrukverificatie geannuleerd door gebruiker"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Te veel pogingen. Gebruik in plaats daarvan de schermvergrendeling."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Te veel pogingen. Gebruik in plaats daarvan de schermvergrendeling."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Kan vingerafdruk niet verwerken. Probeer het opnieuw."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Geen vingerafdrukken geregistreerd"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Dit apparaat heeft geen vingerafdruksensor"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor tijdelijk uitgezet"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Kan vingerafdruksensor niet gebruiken. Ga naar een reparateur."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Aan/uit-knop ingedrukt"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Vingerafdruk gebruiken"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 45b49d373588..d8bcbd10f5f5 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ଟିପଚିହ୍ନ ପ୍ରମାଣିତ ହେଲା"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ମୁହଁ ଚିହ୍ନଟ ହୋଇଛି"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ମୁହଁ ଚିହ୍ନଟ ହୋଇଛି, ଦୟାକରି ସୁନିଶ୍ଚିତ ଦବାନ୍ତୁ"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ଟିପଚିହ୍ନ ହାର୍ଡୱେର ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ଟିପଚିହ୍ନକୁ ସେଟ୍ ଅପ୍ କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ସେଟଅପର ସମୟସୀମା ସମାପ୍ତ ହୋଇଯାଇଛି। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ଟିପଚିହ୍ନ ଅପରେସନକୁ ବାତିଲ କରାଯାଇଛି"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ୟୁଜରଙ୍କ ଦ୍ୱାରା ଟିପଚିହ୍ନ ଅପରେସନକୁ ବାତିଲ କରାଯାଇଛି"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ଅନେକଗୁଡ଼ିଏ ପ୍ରଚେଷ୍ଟା। ଏହା ପରିବର୍ତ୍ତେ ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ଅନେକଗୁଡ଼ିଏ ପ୍ରଚେଷ୍ଟା। ଏହା ପରିବର୍ତ୍ତେ ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ଟିପଚିହ୍ନକୁ ପ୍ରକ୍ରିୟାନ୍ୱିତ କରାଯାଇପାରିବ ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"କୌଣସି ଟିପଚିହ୍ନକୁ ପଞ୍ଜିକରଣ କରାଯାଇନାହିଁ"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ଏହି ଡିଭାଇସ୍‌ରେ ଟିପଚିହ୍ନ ସେନସର୍‌ ନାହିଁ"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"ସେନ୍ସରକୁ ଅସ୍ଥାୟୀ ଭାବେ ଅକ୍ଷମ କରାଯାଇଛି"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ଟିପଚିହ୍ନ ସେନ୍ସରକୁ ବ୍ୟବହାର କରାଯାଇପାରିବ ନାହିଁ। ଏକ ମରାମତି କେନ୍ଦ୍ରକୁ ଭିଜିଟ୍ କରନ୍ତୁ।"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ପାୱାର ବଟନ ଦବାଯାଇଛି"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ଆଙ୍ଗୁଠି <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 2abe228d6733..b762d6621d92 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਮਾਣਿਤ ਹੋਇਆ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ, ਕਿਰਪਾ ਕਰਕੇ \'ਪੁਸ਼ਟੀ ਕਰੋ\' ਦਬਾਓ"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਕਰਨ ਲਈ ਸਮਾਂ ਸਮਾਪਤ ਹੋਇਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੰਬੰਧੀ ਕਾਰਵਾਈ ਰੱਦ ਕੀਤੀ ਗਈ"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੰਬੰਧੀ ਕਾਰਵਾਈ ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਰੱਦ ਕੀਤੀ ਗਈ"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ। ਇਸਦੀ ਬਜਾਏ ਸਕ੍ਰੀਨ ਲਾਕ ਵਰਤੋ।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ। ਇਸਦੀ ਬਜਾਏ ਸਕ੍ਰੀਨ ਲਾਕ ਵਰਤੋ।"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ਫਿੰਗਰਪ੍ਰਿੰਟ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ਕੋਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਰਜ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨਹੀਂ ਹੈ"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"ਸੈਂਸਰ ਕੁਝ ਸਮੇਂ ਲਈ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਮੁਰੰਮਤ ਕਰਨ ਵਾਲੇ ਪ੍ਰਦਾਨਕ ਕੋਲ ਜਾਓ।"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"\'ਪਾਵਰ\' ਬਟਨ ਦਬਾਇਆ ਗਿਆ"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ਉਂਗਲ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 06bff2aaad55..5f3e683d8053 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -668,24 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Uwierzytelniono odciskiem palca"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Twarz rozpoznana"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Twarz rozpoznana, kliknij Potwierdź"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Czytnik linii papilarnych nie jest dostępny"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nie można skonfigurować odcisku palca"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Upłynął limit czasu konfiguracji odcisku palca. Spróbuj ponownie."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Odczyt odcisku palca został anulowany"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Odczyt odcisku palca został anulowany przez użytkownika"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Zbyt wiele prób. Użyj blokady ekranu."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Zbyt wiele prób. Użyj blokady ekranu."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nie udało się przetworzyć odcisku palca. Spróbuj ponownie."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nie zarejestrowano odcisków palców"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"To urządzenie nie jest wyposażone w czytnik linii papilarnych"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Czujnik tymczasowo wyłączony"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Nie można użyć czytnika linii papilarnych. Odwiedź serwis."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Naciśnięto przycisk zasilania"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Odcisk palca <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Używaj odcisku palca"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 43cdfa205162..9887bf6d5db6 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Impressão digital autenticada"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado, pressione \"Confirmar\""</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardware de impressão digital indisponível"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Não foi possível configurar a impressão digital"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Tempo de configuração esgotado. Tente de novo."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operação de impressão digital cancelada"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Operação de impressão digital cancelada pelo usuário"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Excesso de tentativas. Use o bloqueio de tela."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Excesso de tentativas. Use o bloqueio de tela."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Não foi possível processar a impressão digital. Tente de novo."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nenhuma impressão digital registrada"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo não tem um sensor de impressão digital"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor desativado temporariamente"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Não foi possível usar o sensor de impressão digital. Entre em contato com uma assistência técnica."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Botão liga/desliga pressionado"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar impressão digital"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 74df1872a970..9903acb3a4ab 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"A impressão digital foi autenticada."</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado."</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado. Prima Confirmar."</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardware de impressão digital não disponível"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Não é possível configurar a impressão digital"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"A configuração da impressão digital expirou. Tente novamente."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operação de impressão digital cancelada"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Operação de impressão digital cancelada pelo utilizador"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Demasiadas tentativas. Em alternativa, use o bloqueio de ecrã."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiadas tentativas. Em alternativa, use o bloqueio de ecrã."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Não é possível processar a impressão digital. Tente novamente."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nenhuma impressão digital configurada"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo não tem sensor de impressões digitais."</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor temporariamente desativado"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Não é possível usar o sensor de impressões digitais. Visite um fornecedor de serviços de reparação."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Botão ligar/desligar premido"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar a impressão digital"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 43cdfa205162..9887bf6d5db6 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Impressão digital autenticada"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado, pressione \"Confirmar\""</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardware de impressão digital indisponível"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Não foi possível configurar a impressão digital"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Tempo de configuração esgotado. Tente de novo."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operação de impressão digital cancelada"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Operação de impressão digital cancelada pelo usuário"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Excesso de tentativas. Use o bloqueio de tela."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Excesso de tentativas. Use o bloqueio de tela."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Não foi possível processar a impressão digital. Tente de novo."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nenhuma impressão digital registrada"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo não tem um sensor de impressão digital"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor desativado temporariamente"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Não foi possível usar o sensor de impressão digital. Entre em contato com uma assistência técnica."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Botão liga/desliga pressionado"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar impressão digital"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 67ade37b2756..73d92fbe6fd6 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Amprentă autentificată"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Chip autentificat"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Chip autentificat, apasă pe Confirmă"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardware-ul pentru amprentă nu este disponibil"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nu se poate configura amprenta"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Configurarea amprentei a expirat. Încearcă din nou."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operațiunea privind amprenta a fost anulată"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Operațiunea privind amprenta a fost anulată de utilizator"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Prea multe încercări. Folosește blocarea ecranului."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Prea multe încercări. Folosește blocarea ecranului."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nu putem procesa amprenta. Încearcă din nou."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nu au fost înregistrate amprente"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Acest dispozitiv nu are senzor de amprentă"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Senzor dezactivat temporar"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Nu se poate folosi senzorul de amprentă. Vizitează un furnizor de servicii de reparații."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"A fost apăsat butonul de pornire"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Degetul <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Folosește amprenta"</string>
@@ -1172,7 +1166,7 @@
<string name="Midnight" msgid="8176019203622191377">"Miezul nopții"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
- <string name="selectAll" msgid="1532369154488982046">"Selectează-le pe toate"</string>
+ <string name="selectAll" msgid="1532369154488982046">"Selectează tot"</string>
<string name="cut" msgid="2561199725874745819">"Decupează"</string>
<string name="copy" msgid="5472512047143665218">"Copiază"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Eroare la copierea în clipboard"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 7e8df9ff23ff..5b434a56f8ec 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -668,24 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечаток пальца проверен"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицо распознано"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицо распознано, нажмите кнопку \"Подтвердить\""</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Сканер отпечатков пальцев недоступен."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не удалось сохранить отпечаток."</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Время настройки отпечатка пальца истекло. Повторите попытку."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Операция с отпечатком пальца отменена."</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Пользователь отменил операцию с отпечатком пальца."</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Слишком много попыток. Используйте другой способ разблокировки экрана."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Слишком много попыток. Используйте другой способ разблокировки экрана."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не удалось распознать отпечаток пальца. Повторите попытку."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Отпечатки пальцев не добавлены."</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"На этом устройстве нет сканера отпечатков пальцев"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Сканер временно отключен."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Невозможно использовать сканер отпечатков пальцев. Обратитесь в сервисный центр."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Нажата кнопка питания."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Отпечаток <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Использовать отпечаток пальца"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index db7fb6d13b94..5a271180499f 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ඇඟිලි සලකුණ සත්‍යාපනය කරන ලදී"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"මුහුණ සත්‍යාපනය කරන ලදී"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"මුහුණ සත්‍යාපනය කරන ලදී, කරුණාකර තහවුරු කරන්න ඔබන්න"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ඇඟිලි සලකුණු දෘඪාංගය ලද නොහැක"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ඇඟිලි සලකුණ පිහිටුවිය නොහැකිය"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ඇඟිලි සලකුණු පිහිටුවීම කාලය නිමා විය. නැවත උත්සාහ කරන්න."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ඇඟිලි සලකුණු මෙහෙයුම අවලංගු කරන ලදි"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"පරිශීලක විසින් ඇඟිලි සලකුණු මෙහෙයුම අවසන් කරන ලදි"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"උත්සාහ ගණන ඉතා වැඩියි. ඒ වෙනුවට තිර අගුල භාවිත කරන්න."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"උත්සාහ ගණන ඉතා වැඩියි. ඒ වෙනුවට තිර අගුල භාවිත කරන්න."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ඇඟිලි සලකුණ සැකසීමට නොහැක. නැවත උත්සාහ කරන්න."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ඇඟිලි සලකුණු ඇතුළත් කර නොමැත"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"මෙම උපාංගයේ ඇඟිලි සලකුණු සංවේදකයක් නොමැත"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"සංවේදකය තාවකාලිකව අබල කර ඇත"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ඇඟිලි සලකුණු සංවේදකය භාවිත කළ නොහැක. අලුත්වැඩියා සැපයුම්කරුවෙකු වෙත පැමිණෙන්න."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"බල බොත්තම ඔබා ඇත"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ඇඟිලි <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ඇඟිලි සලකුණ භාවිත කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index e199235ac19a..16a56d3156f1 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -668,24 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Odtlačok prsta bol overený"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Tvár bola overená"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Tvár bola overená, stlačte tlačidlo potvrdenia"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardvér na odtlačky prstov nie je k dispozícii"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Odtlačok prsta sa nedá nastaviť"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Nastavenie odtlačku prsta vypršalo. Skúste to znova."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operácia týkajúca sa odtlačku prsta bola zrušená"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Overenie odtlačku prsta zrušil používateľ"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Príliš veľa pokusov. Použite radšej zámku obrazovky."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Príliš veľa pokusov. Použite radšej zámku obrazovky."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Odtlačok prsta sa nedá spracovať. Skúste to znova."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nie sú registrované žiadne odtlačky prstov"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Toto zariadenie nemá senzor odtlačkov prstov"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Senzor je dočasne vypnutý"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Senzor odtlačkov prstov nie je možné používať. Navštívte opravára."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Bol stlačený vypínač"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst: <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Použiť odtlačok prsta"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 305e3903397d..79a275ca7089 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -668,24 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Pristnost prstnega odtisa je preverjena"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Pristnost obraza je potrjena"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Pristnost obraza je preverjena. Pritisnite gumb »Potrdi«."</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Strojna oprema za prstne odtise ni na voljo"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Prstnega odtisa ni mogoče nastaviti."</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Čas za nastavitev prstnega odtisa je potekel. Poskusite znova."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Dejanje s prstnim odtisom je bilo preklicano"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Dejanje s prstnim odtisom je preklical uporabnik"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Preveč poskusov. Odklenite z načinom za zaklepanje zaslona."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Preveč poskusov. Odklenite z zaklepanjem zaslona."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Prstnega odtisa ni mogoče obdelati. Poskusite znova."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Ni prijavljenih prstnih odtisov"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ta naprava nima tipala prstnih odtisov"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Tipalo je začasno onemogočeno"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Tipala prstnih odtisov ni mogoče uporabiti. Obiščite ponudnika popravil."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Gumb za vklop je pritisnjen."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Uporaba prstnega odtisa"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index c6fdb8f9440c..fc2e71590b9a 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Gjurma e gishtit u vërtetua"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Fytyra u vërtetua"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Fytyra u vërtetua, shtyp \"Konfirmo\""</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardueri i gjurmës së gishtit nuk ofrohet"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nuk mund të konfigurohet gjurma e gishtit"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Konfigurimi i gjurmës së gishtit skadoi. Provo përsëri."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Veprimi i gjurmës së gishtit u anulua"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Veprimi i gjurmës së gishtit u anulua nga përdoruesi"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Shumë përpjekje. Përdor më mirë kyçjen e ekranit."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Shumë përpjekje. Përdor më mirë kyçjen e ekranit."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Gjurma e gishtit nuk mund të përpunohet. Provo përsëri."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nuk ka asnjë gjurmë gishti të regjistruar"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Kjo pajisje nuk ka sensor të gjurmës së gishtit"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensori është çaktivizuar përkohësisht"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Sensori i gjurmës së gishtit nuk mund të përdoret. Vizito një ofrues të shërbimit të riparimit."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Butoni i energjisë u shtyp"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Gishti <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Përdor gjurmën e gishtit"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 194e48957a02..4f9ce47d4a90 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -667,24 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Отисак прста је потврђен"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лице је потврђено"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лице је потврђено. Притисните Потврди"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Хардвер за отисак прста није доступан"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Подешавање отиска прста није успело"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Време за подешавање отиска прста је истекло. Пробајте поново."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Радња са отиском прста је отказана"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Корисник је отказао радњу са отиском прста"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Превише покушаја. Користите закључавање екрана уместо тога."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Превише покушаја. Користите закључавање екрана уместо тога."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Обрађивање отиска прста није успело. Пробајте поново."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Није регистрован ниједан отисак прста"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Овај уређај нема сензор за отисак прста"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Сензор је привремено онемогућен"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Не можете да користите сензор за отисак прста. Посетите сервис."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Притиснуто је дугме за укључивање"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Користите отисак прста"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 25109b80a77a..7f0a2014b5f0 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrycket har autentiserats"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansiktet har autentiserats"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansiktet har autentiserats. Tryck på Bekräfta"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Det finns ingen maskinvara för fingeravtryck"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Det gick inte att konfigurera fingeravtryck"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Tiden för fingeravtrycksinställning gick ut. Försök igen."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingeravtrycksåtgärden avbröts"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingeravtrycksåtgärden avbröts av användaren"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"För många försök. Använd låsskärmen i stället."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"För många försök. Använd låsskärmen i stället."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Det gick inte att bearbeta fingeravtrycket. Försök igen."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Det finns inga registrerade fingeravtryck"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Enheten har ingen fingeravtryckssensor"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensorn har tillfälligt inaktiverats"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Det går inte att använda fingeravtryckssensorn. Besök ett reparationsställe."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Av/på-knappen nedtryckt"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Använd ditt fingeravtryck"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 8819f2707593..4d004402a6ba 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Imethibitisha alama ya kidole"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Uso umethibitishwa"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Uso umethibitishwa, tafadhali bonyeza thibitisha"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Maunzi ya kutambua alama ya kidole hayapatikani"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Imeshindwa kuweka mipangilio ya alama ya kidole"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Muda wa kuweka alama ya kidole umeisha. Jaribu tena."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Mchakato wa alama ya kidole umeachwa"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Mtumiaji ameacha mchakato wa uthibitishaji wa alama ya kidole"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Umejaribu mara nyingi mno. Badala yake, tumia mbinu ya kufunga skrini."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Umejaribu mara nyingi mno. Badala yake, tumia mbinu ya kufunga skrini."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Imeshindwa kutambua alama ya kidole. Jaribu tena."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Hakuna alama za vidole zilizojumuishwa"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Kifaa hiki hakina kitambua alama ya kidole"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Kitambuzi kimezimwa kwa muda"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Imeshindwa kutumia kitambuzi cha alama ya kidole. Tembelea mtoa huduma za urekebishaji."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Kitufe cha kuwasha au kuzima kimebonyezwa"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Kidole cha <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Tumia alama ya kidole"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 24307fa95389..f962728c8b30 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"கைரேகை அங்கீகரிக்கப்பட்டது"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"முகம் அங்கீகரிக்கப்பட்டது"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"முகம் அங்கீகரிக்கப்பட்டது. ’உறுதிப்படுத்துக’ என்பதை அழுத்துக"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"கைரேகை பாகம் இல்லை"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"கைரேகையை அமைக்க முடியவில்லை"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"கைரேகை அமைவுக்கான நேரம் முடிந்துவிட்டது. மீண்டும் முயலவும்."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"கைரேகைச் செயல்பாடு ரத்துசெய்யப்பட்டது"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"கைரேகைச் செயல்பாடு பயனரால் ரத்துசெய்யப்பட்டது"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"பலமுறை முயன்றுவிட்டீர்கள். இதற்குப் பதிலாகத் திரைப்பூட்டைப் பயன்படுத்தவும்."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"பலமுறை முயன்றுவிட்டீர்கள். இதற்குப் பதிலாகத் திரைப்பூட்டைப் பயன்படுத்தவும்."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"கைரேகையைச் செயலாக்க முடியவில்லை. மீண்டும் முயலவும்."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"கைரேகைகள் எதுவும் பதிவுசெய்யப்படவில்லை"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"இந்தச் சாதனத்தில் கைரேகை சென்சார் இல்லை"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"சென்சார் தற்காலிகமாக முடக்கப்பட்டுள்ளது"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"கைரேகை சென்சாரைப் பயன்படுத்த முடியவில்லை. பழுதுபார்ப்புச் சேவை வழங்குநரைத் தொடர்புகொள்ளவும்."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"பவர் பட்டன் அழுத்தப்பட்டுள்ளது"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"கைரேகை <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"கைரேகையைப் பயன்படுத்து"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 28b7d2804681..30c28129e8c5 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"వేలిముద్ర ప్రమాణీకరించబడింది"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ముఖం ప్రమాణీకరించబడింది"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ముఖం ప్రమాణీకరించబడింది, దయచేసి ధృవీకరించును నొక్కండి"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"వేలిముద్ర హార్డ్‌వేర్ అందుబాటులో లేదు"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"వేలిముద్రను సెటప్ చేయడం సాధ్యం కాదు"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"వేలిముద్ర సెటప్ సమయం ముగిసింది. మళ్లీ ట్రై చేయండి."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"వేలిముద్ర ఆపరేషన్ రద్దయింది"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"వేలిముద్ర ఆపరేషన్‌ను యూజర్ రద్దు చేశారు"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"చాలా ఎక్కువ సార్లు ప్రయత్నించారు. బదులుగా స్క్రీన్ లాక్‌ను ఉపయోగించండి."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"చాలా ఎక్కువ సార్లు ప్రయత్నించారు. బదులుగా స్క్రీన్ లాక్‌ను ఉపయోగించండి."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"వేలిముద్రను ప్రాసెస్ చేయడం సాధ్యపడదు. మళ్లీ ట్రై చేయండి."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"వేలిముద్రలు ఏవీ ఎన్‌రోల్ అవలేదు"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ఈ పరికరంలో వేలిముద్ర సెన్సార్ లేదు"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"సెన్సార్ తాత్కాలికంగా డిజేబుల్ అయ్యింది"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"వేలిముద్ర సెన్సార్‌ను ఉపయోగించడం సాధ్యం కాదు. రిపెయిర్ ప్రొవైడర్‌ను చూడండి."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>వ వేలు"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"వేలిముద్రను ఉపయోగించండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 94fe59d18549..c92d6139147b 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ตรวจสอบสิทธิ์ลายนิ้วมือแล้ว"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ตรวจสอบสิทธิ์ใบหน้าแล้ว"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ตรวจสอบสิทธิ์ใบหน้าแล้ว โปรดกดยืนยัน"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ฮาร์ดแวร์อ่านลายนิ้วมือไม่พร้อมใช้งาน"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ตั้งค่าลายนิ้วมือไม่ได้"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"การตั้งค่าลายนิ้วมือหมดเวลา โปรดลองอีกครั้ง"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"การทำงานของลายนิ้วมือถูกยกเลิก"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ผู้ใช้ยกเลิกการทำงานของลายนิ้วมือ"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ลองหลายครั้งเกินไป ใช้การล็อกหน้าจอแทน"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ลองหลายครั้งเกินไป ใช้การล็อกหน้าจอแทน"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ประมวลผลลายนิ้วมือไม่ได้ โปรดลองอีกครั้ง"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ไม่มีลายนิ้วมือที่ลงทะเบียน"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"อุปกรณ์นี้ไม่มีเซ็นเซอร์ลายนิ้วมือ"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"เซ็นเซอร์ถูกปิดใช้ชั่วคราว"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ใช้เซ็นเซอร์ลายนิ้วมือไม่ได้ โปรดติดต่อผู้ให้บริการซ่อม"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"กดปุ่มเปิด/ปิดแล้ว"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"นิ้วมือ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ใช้ลายนิ้วมือ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 57c6b2ab3b46..d9443349d691 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Na-authenticate ang fingerprint"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Na-authenticate ang mukha"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Na-authenticate ang mukha, pakipindot ang kumpirmahin"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hindi available ang hardware na ginagamitan ng fingerprint"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Hindi ma-set up ang fingerprint"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Nag-time out ang pag-set up ng fingerprint. Subukan ulit."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Nakansela ang operasyon sa fingerprint"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Kinansela ng user ang operasyon sa fingerprint"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Masyadong maraming pagsubok. Gamitin na lang ang lock ng screen."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Masyadong maraming pagsubok. Gamitin na lang ang lock ng screen."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Hindi maproseso ang fingerprint. Subukan ulit."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Walang naka-enroll na fingerprint"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Walang sensor para sa fingerprint ang device na ito"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Pansamantalang na-disable ang sensor"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Hindi magamit ang sensor para sa fingerprint. Bumisita sa provider ng pag-aayos."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Napindot ang power button"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Daliri <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gumamit ng fingerprint"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index af18dacc05c2..dfae3016beb3 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Parmak izi kimlik doğrulaması yapıldı"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Yüz kimliği doğrulandı"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Yüz kimliği doğrulandı, lütfen onayla\'ya basın"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Parmak izi donanımı kullanılamıyor"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Parmak izi ayarlanamıyor"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Parmak izi kurulumu zaman aşımına uğradı. Tekrar deneyin."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Parmak izi işlemi iptal edildi"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Parmak izi işlemi kullanıcı tarafından iptal edildi"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Çok fazla deneme yapıldı. Bunun yerine ekran kilidini kullanın."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Çok fazla deneme yapıldı. Bunun yerine ekran kilidini kullanın."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Parmak izi işlenemiyor. Tekrar deneyin."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Parmak izi kaydedilmedi"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Bu cihazda parmak izi sensörü yok"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensör geçici olarak devre dışı bırakıldı"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Parmak izi sensörü kullanılamıyor. Bir onarım hizmeti sağlayıcıyı ziyaret edin."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Güç düğmesine basıldı"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. parmak"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Parmak izi kullan"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 5b645db69daa..c8fa2108e6d3 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -668,24 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Відбиток пальця автентифіковано"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Обличчя автентифіковано"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Обличчя автентифіковано. Натисніть \"Підтвердити\""</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Сканер відбитків пальців недоступний"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не вдалося створити відбиток пальця"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Час очікування для налаштування відбитка пальця минув. Повторіть спробу."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Дію з відбитком пальця скасовано"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Користувач скасував дію з відбитком пальця"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Забагато спроб. Використайте натомість розблокування екрана."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Забагато спроб. Використайте натомість розблокування екрана."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не вдалось обробити відбиток пальця. Повторіть спробу."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Відбитки пальців не зареєстровано"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"На цьому пристрої немає сканера відбитків пальців"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Сканер тимчасово вимкнено"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Не вдається скористатися сканером відбитків пальців. Зверніться до постачальника послуг із ремонту."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Натиснуто кнопку живлення"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Відбиток пальця <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Доступ за відбитком пальця"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 1c9275293cca..5ad1f1832077 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"فنگر پرنٹ کی تصدیق ہو گئی"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چہرے کی تصدیق ہو گئی"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چہرے کی تصدیق ہو گئی، براہ کرم \'تصدیق کریں\' کو دبائيں"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"فنگر پرنٹ ہارڈ ویئر دستیاب نہیں ہے"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"فنگر پرنٹ کو سیٹ اپ نہیں کیا جا سکا"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"فنگر پرنٹ کے سیٹ اپ کا وقت ختم ہو گیا۔ دوبارہ کوشش کریں۔"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"فنگر پرنٹ کی کارروائی منسوخ ہوگئی"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"صارف نے فنگر پرنٹ کی کارروائی منسوخ کر دی"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"کافی زیادہ کوششیں۔ اس کے بجائے اسکرین لاک کا استعمال کریں۔"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"کافی زیادہ کوششیں۔ اس کے بجائے اسکرین لاک کا استعمال کریں۔"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"فنگر پرنٹ پروسیس نہیں ہو سکتا۔ دوبارہ کوشش کریں۔"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"کوئی فنگر پرنٹ مندرج نہیں ہے"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"اس آلہ میں فنگر پرنٹ سینسر نہیں ہے"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"سینسر عارضی طور غیر فعال ہے"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"فنگر پرنٹ سینسر کو استعمال نہیں کیا جا سکتا۔ مرمت فراہم کنندہ کو ملاحظہ کریں۔"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"پاور بٹن دبایا گیا"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"انگلی <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"فنگر پرنٹ استعمال کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 828f391345f0..a3ffb9c62ad5 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Barmoq izi tekshirildi"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Yuzingiz aniqlandi"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Yuzingiz aniqlandi, tasdiqlash uchun bosing"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Barmoq izi skaneri ish holatida emas"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Barmoq izi sozlanmadi"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Barmoq izini sozlash vaqti tugadi. Qayta urining."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Barmoq izi amali bekor qilindi"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Barmoq izi amali foydalanuvchi tomonidan bekor qilindi"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Juda koʻp urinildi. Ekran qulfi orqali urining."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Juda koʻp urinildi. Ekran qulfi orqali urining."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Barmoq izi tekshirilmadi. Qayta urining."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Hech qanday barmoq izi qayd qilinmagan"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Bu qurilmada barmoq izi skaneri yo‘q"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Skaner vaqtincha faol emas"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Barmoq izi skaneridan foydalanish imkonsiz. Xizmat koʻrsatish markaziga murojaat qiling."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Quvvat tugmasi bosildi"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Barmoq izi <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Barmoq izi ishlatish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 2e57cafaec1f..8f52be0cb10d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Đã xác thực vân tay"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Đã xác thực khuôn mặt"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Đã xác thực khuôn mặt, vui lòng nhấn để xác nhận"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Không có phần cứng xử lý vân tay"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Không thể thiết lập vân tay"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Hết thời gian chờ thiết lập vân tay. Hãy thử lại."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Đã huỷ thao tác dùng vân tay"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Người dùng đã huỷ thao tác sử dụng vân tay"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Bạn đã thử quá nhiều lần. Hãy dùng phương thức khoá màn hình."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Bạn đã thử quá nhiều lần. Hãy dùng phương thức khoá màn hình."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Không xử lý được vân tay. Hãy thử lại."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Chưa đăng ký vân tay"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Thiết bị này không có cảm biến vân tay"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Đã tắt tạm thời cảm biến"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Không dùng được cảm biến vân tay. Hãy liên hệ với một nhà cung cấp dịch vụ sửa chữa."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Đã nhấn nút nguồn"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Ngón tay <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Dùng vân tay"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 0d49373867fb..28f563d2f170 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"已验证指纹"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"面孔已验证"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"面孔已验证,请按确认按钮"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"无法使用指纹硬件"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"无法设置指纹"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"指纹设置已超时,请重试。"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"已取消指纹操作"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"用户取消了指纹操作"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"尝试次数过多,请通过屏幕锁定功能解锁。"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"尝试次数过多,请通过屏幕锁定功能解锁。"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"无法处理指纹,请重试。"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"未注册任何指纹"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"此设备没有指纹传感器"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"已暂时停用传感器"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"无法使用指纹传感器。请联系维修服务提供商。"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"已按下电源按钮"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指纹"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index cce19905c2ff..59a2abd442eb 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"驗證咗指紋"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"面孔已經驗證"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"面孔已經驗證,請㩒一下 [確認]"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"無法使用指紋硬件"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"無法設定指紋"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋設定逾時,請再試一次。"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"指紋操作已取消"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"使用者已取消指紋操作"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"嘗試次數過多,請改用螢幕鎖定功能。"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"嘗試次數過多,請改用螢幕鎖定功能。"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"無法處理指紋,請再試一次。"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"尚未註冊任何指紋"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"此裝置沒有指紋感應器"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"感應器已暫時停用"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"指紋感應器無法使用,請諮詢維修服務供應商。"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"已按下開關按鈕"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指紋鎖定"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index ff31b74fbea2..2233668ba404 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"指紋驗證成功"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"臉孔驗證成功"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"臉孔驗證成功,請按下 [確認] 按鈕"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"指紋辨識硬體無法使用"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"無法設定指紋"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋設定逾時,請再試一次。"</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"指紋作業已取消"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"使用者已取消指紋驗證作業"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"嘗試次數過多,請改用螢幕鎖定功能。"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"嘗試次數過多,請改用螢幕鎖定功能。"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"無法處理指紋,請再試一次。"</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"未登錄任何指紋"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"這個裝置沒有指紋感應器"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"感應器已暫時停用"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"指紋感應器無法使用,請洽詢維修供應商。"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"已按下電源鍵"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指紋"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 5112da966e7f..176c3743825d 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -666,24 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Izigxivizo zeminwe zigunyaziwe"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ubuso bufakazelwe ubuqiniso"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ukuqinisekiswa kobuso, sicela ucindezele okuthi qinisekisa"</string>
- <!-- no translation found for fingerprint_error_hw_not_available (7755729484334001137) -->
- <skip />
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Izingxenyekazi zekhompuyutha zezigxivizo zeminwe azitholakali"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Ayikwazi ukusetha izigxivizo zeminwe"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Ukusethwa kwesigxivizo somunwe kuphelelwe yisikhathi Zama futhi."</string>
- <!-- no translation found for fingerprint_error_canceled (5541771463159727513) -->
- <skip />
- <!-- no translation found for fingerprint_error_user_canceled (2017941773466506863) -->
- <skip />
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Ukusebenza kwezigxivizo zeminwe kukhanseliwe"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Umsebenzi wezigxivizo zeminwe ukhanselwe umsebenzisi"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Imizamo eminingi kakhulu. Sebenzisa ukukhiya isikrini kunalokho."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Imizamo eminingi kakhulu. Sebenzisa ukukhiya isikrini kunalokho."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Ayikwazi ukucubungula isigxivizo somunwe. Zama futhi."</string>
- <!-- no translation found for fingerprint_error_no_fingerprints (3144806556204061862) -->
- <skip />
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Azikho izigxivizo zeminwe ezibhalisiwe"</string>
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Le divayisi ayinayo inzwa yezigxivizo zeminwe"</string>
- <!-- no translation found for fingerprint_error_security_update_required (8440349108169661934) -->
- <skip />
- <!-- no translation found for fingerprint_error_bad_calibration (6770614925736183528) -->
- <skip />
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Inzwa ikhutshazwe okwesikhashana"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Ayikwazi ukusebenzisa inzwa yesigxivizo somunwe. Vakashela umhlinzeki wokulungisa."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Inkinobho yamandla icindezelwe"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Umunwe ongu-<xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Sebenzisa izigxivizo zeminwe"</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e9996955a9c0..59066eb83f1c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5448,6 +5448,13 @@
<!-- Title for button to launch the personal safety app to make an emergency call -->
<string name="work_mode_emergency_call_button">Emergency</string>
+ <!-- Title of the alert dialog prompting the user to set up a screen lock [CHAR LIMIT=30] -->
+ <string name="set_up_screen_lock_title">Set a screen lock</string>
+ <!-- Action label for the dialog prompting the user to set up a screen lock [CHAR LIMIT=30] -->
+ <string name="set_up_screen_lock_action_label">Set screen lock</string>
+ <!-- Message shown in the dialog prompting the user to set up a screen lock to access private space [CHAR LIMIT=30] -->
+ <string name="private_space_set_up_screen_lock_message">To use your private space, set a screen lock on this device</string>
+
<!-- Title of the dialog that is shown when the user tries to launch a blocked application [CHAR LIMIT=50] -->
<string name="app_blocked_title">App is not available</string>
<!-- Default message shown in the dialog that is shown when the user tries to launch a blocked application [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cf9c02a93267..3284791ef384 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3247,6 +3247,12 @@
<java-symbol type="string" name="work_mode_off_title" />
<java-symbol type="string" name="work_mode_turn_on" />
+ <!-- Alert dialog prompting the user to set up a screen lock -->
+ <java-symbol type="string" name="set_up_screen_lock_title" />
+ <java-symbol type="string" name="set_up_screen_lock_action_label" />
+ <!-- Message for the alert dialog prompting the user to set up a screen lock to access private space -->
+ <java-symbol type="string" name="private_space_set_up_screen_lock_message" />
+
<java-symbol type="string" name="deprecated_target_sdk_message" />
<java-symbol type="string" name="deprecated_target_sdk_app_store" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index ebf4cca9ebc2..a7d083cbc399 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -988,7 +988,7 @@ public class ActivityThreadTest {
@Override
public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) {
- if (mPipUiStateLatch != null && pipState.isEnteringPip()) {
+ if (mPipUiStateLatch != null && pipState.isTransitioningToPip()) {
mPipUiStateLatch.countDown();
}
}
diff --git a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
index d816d0853ab6..34f5841aef72 100644
--- a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
@@ -194,13 +194,13 @@ public class FaceManagerTest {
new CancellationSignal(), mEnrollmentCallback, null /* disabledFeatures */);
verify(mService).enroll(eq(USER_ID), any(), any(), any(), anyString(), any(), any(),
- anyBoolean());
+ anyBoolean(), any());
mFaceManager.enroll(USER_ID, new byte[]{},
new CancellationSignal(), mEnrollmentCallback, null /* disabledFeatures */);
verify(mService, atMost(1 /* maxNumberOfInvocations */)).enroll(eq(USER_ID), any(), any(),
- any(), anyString(), any(), any(), anyBoolean());
+ any(), anyString(), any(), any(), anyBoolean(), any());
verify(mEnrollmentCallback).onEnrollmentError(eq(FACE_ERROR_HW_UNAVAILABLE), anyString());
}
@@ -213,7 +213,7 @@ public class FaceManagerTest {
verify(mEnrollmentCallback).onEnrollmentError(eq(FACE_ERROR_UNABLE_TO_PROCESS),
anyString());
verify(mService, never()).enroll(eq(USER_ID), any(), any(),
- any(), anyString(), any(), any(), anyBoolean());
+ any(), anyString(), any(), any(), anyBoolean(), any());
}
@Test
diff --git a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
index 70313b8c9ea7..ce7d6a95c2f4 100644
--- a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
@@ -162,11 +162,13 @@ public class FingerprintManagerTest {
mCaptor.getValue().onAllAuthenticatorsRegistered(mProps);
mFingerprintManager.enroll(null, new CancellationSignal(), USER_ID,
- mEnrollCallback, FingerprintManager.ENROLL_ENROLL);
+ mEnrollCallback, FingerprintManager.ENROLL_ENROLL,
+ (new FingerprintEnrollOptions.Builder()).build());
verify(mEnrollCallback).onEnrollmentError(eq(FINGERPRINT_ERROR_UNABLE_TO_PROCESS),
anyString());
- verify(mService, never()).enroll(any(), any(), anyInt(), any(), anyString(), anyInt());
+ verify(mService, never()).enroll(any(), any(), anyInt(), any(), anyString(), anyInt(),
+ any());
}
@Test
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 5649e7118fd5..f60eff69690a 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -16,6 +16,11 @@
package android.text;
+import static com.android.graphics.hwui.flags.Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -26,11 +31,16 @@ import static org.junit.Assert.fail;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.text.Layout.Alignment;
import android.text.style.StrikethroughSpan;
@@ -38,6 +48,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,6 +60,9 @@ import java.util.Locale;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class LayoutTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private static final int LINE_COUNT = 5;
private static final int LINE_HEIGHT = 12;
private static final int LINE_DESCENT = 4;
@@ -638,22 +652,268 @@ public class LayoutTest {
}
}
- private final class MockCanvas extends Canvas {
+ @Test
+ @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextEnabled_testDrawSelectionAndHighlight_drawsHighContrastSelectionAndHighlight() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd);
+
+ List<Path> highlightPaths = new ArrayList<>();
+ List<Paint> highlightPaints = new ArrayList<>();
+
+ Path selectionPath = new Path();
+ RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT);
+ selectionPath.addRect(selectionRect, Path.Direction.CW);
+ highlightPaths.add(selectionPath);
+
+ Paint selectionPaint = new Paint();
+ selectionPaint.setColor(Color.CYAN);
+ highlightPaints.add(selectionPaint);
+
+ final int width = 256;
+ final int height = 256;
+ MockCanvas c = new MockCanvas(width, height);
+ c.setHighContrastTextEnabled(true);
+ layout.draw(c, highlightPaths, highlightPaints, selectionPath, selectionPaint,
+ /* cursorOffsetVertical= */ 0);
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ var textsDrawn = LINE_COUNT;
+ var highlightsDrawn = 2;
+ assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+
+ var highlightsFound = 0;
+ var curLineIndex = 0;
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+ if (drawCommand.path != null) {
+ assertThat(drawCommand.path).isEqualTo(selectionPath);
+ assertThat(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+ assertThat(drawCommand.paint.getBlendMode()).isNotNull();
+ highlightsFound++;
+ } else if (drawCommand.text != null) {
+ int start = layout.getLineStart(curLineIndex);
+ int end = layout.getLineEnd(curLineIndex);
+ assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
+ curLineIndex++;
+
+ assertWithMessage("highlight is drawn on top of text")
+ .that(highlightsFound).isEqualTo(0);
+ }
+ }
+
+ assertThat(highlightsFound).isEqualTo(2);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextEnabled_testDrawHighlight_drawsHighContrastHighlight() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd);
+
+ List<Path> highlightPaths = new ArrayList<>();
+ List<Paint> highlightPaints = new ArrayList<>();
+
+ Path selectionPath = new Path();
+ RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT);
+ selectionPath.addRect(selectionRect, Path.Direction.CW);
+ highlightPaths.add(selectionPath);
+
+ Paint selectionPaint = new Paint();
+ selectionPaint.setColor(Color.CYAN);
+ highlightPaints.add(selectionPaint);
+
+ final int width = 256;
+ final int height = 256;
+ MockCanvas c = new MockCanvas(width, height);
+ c.setHighContrastTextEnabled(true);
+ layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
+ /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ var textsDrawn = LINE_COUNT;
+ var highlightsDrawn = 1;
+ assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+
+ var highlightsFound = 0;
+ var curLineIndex = 0;
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+ if (drawCommand.path != null) {
+ assertThat(drawCommand.path).isEqualTo(selectionPath);
+ assertThat(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+ assertThat(drawCommand.paint.getBlendMode()).isNotNull();
+ highlightsFound++;
+ } else if (drawCommand.text != null) {
+ int start = layout.getLineStart(curLineIndex);
+ int end = layout.getLineEnd(curLineIndex);
+ assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
+ curLineIndex++;
+
+ assertWithMessage("highlight is drawn on top of text")
+ .that(highlightsFound).isEqualTo(0);
+ }
+ }
+
+ assertThat(highlightsFound).isEqualTo(1);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextDisabledByDefault_testDrawHighlight_drawsNormalHighlightBehind() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd);
+
+ List<Path> highlightPaths = new ArrayList<>();
+ List<Paint> highlightPaints = new ArrayList<>();
+
+ Path selectionPath = new Path();
+ RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT);
+ selectionPath.addRect(selectionRect, Path.Direction.CW);
+ highlightPaths.add(selectionPath);
+
+ Paint selectionPaint = new Paint();
+ selectionPaint.setColor(Color.CYAN);
+ highlightPaints.add(selectionPaint);
+
+ final int width = 256;
+ final int height = 256;
+ MockCanvas c = new MockCanvas(width, height);
+ layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
+ /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ var textsDrawn = LINE_COUNT;
+ var highlightsDrawn = 1;
+ assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+
+ var highlightsFound = 0;
+ var curLineIndex = 0;
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+ if (drawCommand.path != null) {
+ assertThat(drawCommand.path).isEqualTo(selectionPath);
+ assertThat(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+ assertThat(drawCommand.paint.getBlendMode()).isNull();
+ highlightsFound++;
+ } else if (drawCommand.text != null) {
+ int start = layout.getLineStart(curLineIndex);
+ int end = layout.getLineEnd(curLineIndex);
+ assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
+ curLineIndex++;
+
+ assertWithMessage("highlight is drawn behind text")
+ .that(highlightsFound).isGreaterThan(0);
+ }
+ }
+
+ assertThat(highlightsFound).isEqualTo(1);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextEnabledButFlagOff_testDrawHighlight_drawsNormalHighlightBehind() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd);
+
+ List<Path> highlightPaths = new ArrayList<>();
+ List<Paint> highlightPaints = new ArrayList<>();
+
+ Path selectionPath = new Path();
+ RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT);
+ selectionPath.addRect(selectionRect, Path.Direction.CW);
+ highlightPaths.add(selectionPath);
+
+ Paint selectionPaint = new Paint();
+ selectionPaint.setColor(Color.CYAN);
+ highlightPaints.add(selectionPaint);
+
+ final int width = 256;
+ final int height = 256;
+ MockCanvas c = new MockCanvas(width, height);
+ c.setHighContrastTextEnabled(true);
+ layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
+ /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ var textsDrawn = LINE_COUNT;
+ var highlightsDrawn = 1;
+ assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
- class DrawCommand {
+ var highlightsFound = 0;
+ var curLineIndex = 0;
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+ if (drawCommand.path != null) {
+ assertThat(drawCommand.path).isEqualTo(selectionPath);
+ assertThat(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+ assertThat(drawCommand.paint.getBlendMode()).isNull();
+ highlightsFound++;
+ } else if (drawCommand.text != null) {
+ int start = layout.getLineStart(curLineIndex);
+ int end = layout.getLineEnd(curLineIndex);
+ assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
+ curLineIndex++;
+
+ assertWithMessage("highlight is drawn behind text")
+ .that(highlightsFound).isGreaterThan(0);
+ }
+ }
+
+ assertThat(highlightsFound).isEqualTo(1);
+ }
+
+ @Test
+ public void mockCanvasHighContrastOverridesCorrectly() {
+ var canvas = new MockCanvas(100, 100);
+
+ assertThat(canvas.isHighContrastTextEnabled()).isFalse();
+ canvas.setHighContrastTextEnabled(true);
+ assertThat(canvas.isHighContrastTextEnabled()).isTrue();
+ canvas.setHighContrastTextEnabled(false);
+ assertThat(canvas.isHighContrastTextEnabled()).isFalse();
+ }
+
+ private static final class MockCanvas extends Canvas {
+
+ static class DrawCommand {
public final String text;
public final float x;
public final float y;
+ public final Path path;
+ public final Paint paint;
- DrawCommand(String text, float x, float y) {
+ DrawCommand(String text, float x, float y, Paint paint) {
this.text = text;
this.x = x;
this.y = y;
+ this.paint = paint;
+ path = null;
+ }
+
+ DrawCommand(Path path, Paint paint) {
+ this.path = path;
+ this.paint = paint;
+ y = 0;
+ x = 0;
+ text = null;
}
}
List<DrawCommand> mDrawCommands;
+ private Boolean mIsHighContrastTextOverride = null;
+
+ public void setHighContrastTextEnabled(boolean enabled) {
+ mIsHighContrastTextOverride = enabled;
+ }
+
+ @Override
+ public boolean isHighContrastTextEnabled() {
+ return mIsHighContrastTextOverride == null ? super.isHighContrastTextEnabled()
+ : mIsHighContrastTextOverride;
+ }
+
MockCanvas(int width, int height) {
super();
mDrawCommands = new ArrayList<>();
@@ -666,7 +926,7 @@ public class LayoutTest {
@Override
public void drawText(String text, int start, int end, float x, float y, Paint p) {
- mDrawCommands.add(new DrawCommand(text.substring(start, end), x, y));
+ mDrawCommands.add(new DrawCommand(text.substring(start, end), x, y, p));
}
@Override
@@ -676,7 +936,7 @@ public class LayoutTest {
@Override
public void drawText(char[] text, int index, int count, float x, float y, Paint p) {
- mDrawCommands.add(new DrawCommand(new String(text, index, count), x, y));
+ mDrawCommands.add(new DrawCommand(new String(text, index, count), x, y, p));
}
@Override
@@ -691,6 +951,11 @@ public class LayoutTest {
drawText(text, index, count, x, y, paint);
}
+ @Override
+ public void drawPath(Path path, Paint p) {
+ mDrawCommands.add(new DrawCommand(path, p));
+ }
+
List<DrawCommand> getDrawCommands() {
return mDrawCommands;
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
index 4f5f3c0ddeaf..7dd9e55f8f3e 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
@@ -106,6 +106,13 @@ public class PrebakedSegmentTest {
}
@Test
+ public void testScaleLinearly_ignoresAndReturnsSameEffect() {
+ PrebakedSegment prebaked = new PrebakedSegment(
+ VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+ assertSame(prebaked, prebaked.scaleLinearly(0.5f));
+ }
+
+ @Test
public void testDuration() {
assertEquals(-1, new PrebakedSegment(
VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
index ec5a084c2ddb..e9a08aef4856 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
@@ -129,6 +129,27 @@ public class PrimitiveSegmentTest {
}
@Test
+ public void testScaleLinearly() {
+ PrimitiveSegment initial = new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0);
+
+ assertEquals(1f, initial.scaleLinearly(1).getScale(), TOLERANCE);
+ assertEquals(0.5f, initial.scaleLinearly(0.5f).getScale(), TOLERANCE);
+ assertEquals(1f, initial.scaleLinearly(1.5f).getScale(), TOLERANCE);
+ assertEquals(0.8f, initial.scaleLinearly(0.8f).getScale(), TOLERANCE);
+ // Restores back to the exact original value since this is a linear scaling.
+ assertEquals(1f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getScale(), TOLERANCE);
+
+ initial = new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_CLICK, 0, 0);
+
+ assertEquals(0f, initial.scaleLinearly(1).getScale(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.5f).getScale(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).getScale(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).scaleLinearly(2 / 3f).getScale(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getScale(), TOLERANCE);
+ }
+
+ @Test
public void testDuration() {
assertEquals(-1, new PrimitiveSegment(
VibrationEffect.Composition.PRIMITIVE_NOOP, 1, 10).getDuration());
diff --git a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
index 5caa86bb9fb5..01013ab69f85 100644
--- a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
@@ -131,6 +131,37 @@ public class RampSegmentTest {
}
@Test
+ public void testScaleLinearly() {
+ RampSegment initial = new RampSegment(0, 1, 0, 0, 0);
+
+ assertEquals(0f, initial.scaleLinearly(1f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).scaleLinearly(2 / 3f).getStartAmplitude(),
+ TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getStartAmplitude(),
+ TOLERANCE);
+
+ assertEquals(1f, initial.scaleLinearly(1f).getEndAmplitude(), TOLERANCE);
+ assertEquals(0.5f, initial.scaleLinearly(0.5f).getEndAmplitude(), TOLERANCE);
+ assertEquals(1f, initial.scaleLinearly(1.5f).getEndAmplitude(), TOLERANCE);
+ assertEquals(0.8f, initial.scaleLinearly(0.8f).getEndAmplitude(), TOLERANCE);
+ // Restores back to the exact original value since this is a linear scaling.
+ assertEquals(0.8f, initial.scaleLinearly(1.5f).scaleLinearly(0.8f).getEndAmplitude(),
+ TOLERANCE);
+
+ initial = new RampSegment(0.5f, 1, 0, 0, 0);
+
+ assertEquals(0.5f, initial.scaleLinearly(1).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.25f, initial.scaleLinearly(0.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.75f, initial.scaleLinearly(1.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.4f, initial.scaleLinearly(0.8f).getStartAmplitude(), TOLERANCE);
+ // Restores back to the exact original value since this is a linear scaling.
+ assertEquals(0.5f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getStartAmplitude(),
+ TOLERANCE);
+ }
+
+ @Test
public void testDuration() {
assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration());
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
index 44db30603089..40776abc0291 100644
--- a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
@@ -141,6 +141,37 @@ public class StepSegmentTest {
}
@Test
+ public void testScaleLinearly_fullAmplitude() {
+ StepSegment initial = new StepSegment(1f, 0, 0);
+
+ assertEquals(1f, initial.scaleLinearly(1).getAmplitude(), TOLERANCE);
+ assertEquals(0.5f, initial.scaleLinearly(0.5f).getAmplitude(), TOLERANCE);
+ assertEquals(1f, initial.scaleLinearly(1.5f).getAmplitude(), TOLERANCE);
+ assertEquals(0.8f, initial.scaleLinearly(0.8f).getAmplitude(), TOLERANCE);
+ // Restores back to the exact original value since this is a linear scaling.
+ assertEquals(1f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getAmplitude(),
+ TOLERANCE);
+
+ initial = new StepSegment(0, 0, 0);
+
+ assertEquals(0f, initial.scaleLinearly(1).getAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.5f).getAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).getAmplitude(), TOLERANCE);
+ }
+
+ @Test
+ public void testScaleLinearly_defaultAmplitude() {
+ StepSegment initial = new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0);
+
+ assertEquals(VibrationEffect.DEFAULT_AMPLITUDE, initial.scaleLinearly(1).getAmplitude(),
+ TOLERANCE);
+ assertEquals(VibrationEffect.DEFAULT_AMPLITUDE, initial.scaleLinearly(0.5f).getAmplitude(),
+ TOLERANCE);
+ assertEquals(VibrationEffect.DEFAULT_AMPLITUDE, initial.scaleLinearly(1.5f).getAmplitude(),
+ TOLERANCE);
+ }
+
+ @Test
public void testDuration() {
assertEquals(5, new StepSegment(0, 0, 5).getDuration());
}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index f3bb21719890..b6ce9b64323b 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -153,6 +153,18 @@ public class Canvas extends BaseCanvas {
}
/**
+ * Indicates whether this Canvas is drawing high contrast text.
+ *
+ * @see android.view.accessibility.AccessibilityManager#isHighTextContrastEnabled()
+ * @return True if high contrast text is enabled, false otherwise.
+ *
+ * @hide
+ */
+ public boolean isHighContrastTextEnabled() {
+ return nIsHighContrastText(mNativeCanvasWrapper);
+ }
+
+ /**
* Specify a bitmap for the canvas to draw into. All canvas state such as
* layers, filters, and the save/restore stack are reset. Additionally,
* the canvas' target density is updated to match that of the bitmap.
@@ -1452,6 +1464,8 @@ public class Canvas extends BaseCanvas {
@CriticalNative
private static native boolean nIsOpaque(long canvasHandle);
@CriticalNative
+ private static native boolean nIsHighContrastText(long canvasHandle);
+ @CriticalNative
private static native int nGetWidth(long canvasHandle);
@CriticalNative
private static native int nGetHeight(long canvasHandle);
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
index e04ab817215c..34f03c2f226b 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
@@ -23,7 +23,8 @@
<com.android.wm.shell.bubbles.bar.BubbleBarHandleView
android:id="@+id/bubble_bar_handle_view"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content" />
+ android:layout_height="@dimen/bubble_bar_expanded_view_caption_height"
+ android:layout_width="@dimen/bubble_bar_expanded_view_caption_width"
+ android:layout_gravity="top|center_horizontal" />
</com.android.wm.shell.bubbles.bar.BubbleBarExpandedView>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index f73775becac9..cbfa74e9332b 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -244,6 +244,8 @@
<dimen name="bubble_popup_padding">24dp</dimen>
<!-- The size of the caption bar inset at the top of bubble bar expanded view. -->
<dimen name="bubble_bar_expanded_view_caption_height">32dp</dimen>
+ <!-- The width of the caption bar at the top of bubble bar expanded view. -->
+ <dimen name="bubble_bar_expanded_view_caption_width">128dp</dimen>
<!-- The height of the dots shown for the caption menu in the bubble bar expanded view.. -->
<dimen name="bubble_bar_expanded_view_caption_dot_size">4dp</dimen>
<!-- The spacing between the dots for the caption menu in the bubble bar expanded view.. -->
@@ -501,6 +503,17 @@
fullscreen if dragged until the top bound of the task is within the area. -->
<dimen name="desktop_mode_transition_area_height">16dp</dimen>
+ <!-- The width of the area where a desktop task will transition to fullscreen. -->
+ <dimen name="desktop_mode_fullscreen_from_desktop_width">80dp</dimen>
+
+ <!-- The height of the area where a desktop task will transition to fullscreen. -->
+ <dimen name="desktop_mode_fullscreen_from_desktop_height">40dp</dimen>
+
+ <!-- The height on the screen where drag to the left or right edge will result in a
+ desktop task snapping to split size. The empty space between this and the top is to allow
+ for corner drags without transition. -->
+ <dimen name="desktop_mode_split_from_desktop_height">100dp</dimen>
+
<!-- The acceptable area ratio of fg icon area/bg icon area, i.e. (72 x 72) / (108 x 108) -->
<item type="dimen" format="float" name="splash_icon_enlarge_foreground_threshold">0.44</item>
<!-- Scaling factor applied to splash icons without provided background i.e. (192 / 160) -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index fe65fdd30e48..d8d0d876b4f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -643,19 +643,6 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
}
- /** Helper to set int metadata on the Surface corresponding to the task id. */
- public void setSurfaceMetadata(int taskId, int key, int value) {
- synchronized (mLock) {
- final TaskAppearedInfo info = mTasks.get(taskId);
- if (info == null || info.getLeash() == null) {
- return;
- }
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.setMetadata(info.getLeash(), key, value);
- t.apply();
- }
- }
-
private boolean updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash,
TaskListener oldListener, TaskListener newListener) {
if (oldListener == newListener) return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 34be9b097e81..2606fb661e80 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -114,6 +114,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
/** Tracks if we should start the back gesture on the next motion move event */
private boolean mShouldStartOnNextMoveEvent = false;
private boolean mOnBackStartDispatched = false;
+ private boolean mPointerPilfered = false;
private final FlingAnimationUtils mFlingAnimationUtils;
@@ -404,11 +405,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@VisibleForTesting
void onPilferPointers() {
+ mPointerPilfered = true;
// Dispatch onBackStarted, only to app callbacks.
// System callbacks will receive onBackStarted when the remote animation starts.
if (!shouldDispatchToAnimator() && mActiveCallback != null) {
mCurrentTracker.updateStartLocation();
- tryDispatchAppOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
+ tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
}
}
@@ -511,7 +513,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
// App is handling back animation. Cancel system animation latency tracking.
cancelLatencyTracking();
- tryDispatchAppOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
+ tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
}
}
@@ -555,14 +557,13 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
&& mBackNavigationInfo.isPrepareRemoteAnimation();
}
- private void tryDispatchAppOnBackStarted(
+ private void tryDispatchOnBackStarted(
IOnBackInvokedCallback callback,
BackMotionEvent backEvent) {
- if (mOnBackStartDispatched && callback != null) {
+ if (mOnBackStartDispatched || callback == null || !mPointerPilfered) {
return;
}
dispatchOnBackStarted(callback, backEvent);
- mOnBackStartDispatched = true;
}
private void dispatchOnBackStarted(
@@ -573,6 +574,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
try {
callback.onBackStarted(backEvent);
+ mOnBackStartDispatched = true;
} catch (RemoteException e) {
Log.e(TAG, "dispatchOnBackStarted error: ", e);
}
@@ -872,6 +874,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mActiveCallback = null;
mShouldStartOnNextMoveEvent = false;
mOnBackStartDispatched = false;
+ mPointerPilfered = false;
mShellBackAnimationRegistry.resetDefaultCrossActivity();
cancelLatencyTracking();
if (mBackNavigationInfo != null) {
@@ -957,15 +960,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mCurrentTracker.updateStartLocation();
BackMotionEvent startEvent =
mCurrentTracker.createStartEvent(apps[0]);
- // {@code mActiveCallback} is the callback from
- // the BackAnimationRunners and not a real app-side
- // callback. We also dispatch to the app-side callback
- // (which should be a system callback with PRIORITY_SYSTEM)
- // to keep consistent with app registered callbacks.
dispatchOnBackStarted(mActiveCallback, startEvent);
- tryDispatchAppOnBackStarted(
- mBackNavigationInfo.getOnBackInvokedCallback(),
- startEvent);
}
// Dispatch the first progress after animation start for
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 5c6f73f0a4a2..e0f0556d03f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -481,7 +481,12 @@ public class BubbleController implements ConfigurationChangeListener,
});
mOneHandedOptional.ifPresent(this::registerOneHandedState);
- mDragAndDropController.addListener(this::collapseStack);
+ mDragAndDropController.addListener(new DragAndDropController.DragAndDropListener() {
+ @Override
+ public void onDragStarted() {
+ collapseStack();
+ }
+ });
// Clear out any persisted bubbles on disk that no longer have a valid user.
List<UserInfo> users = mUserManager.getAliveUsers();
@@ -1838,7 +1843,7 @@ public class BubbleController implements ConfigurationChangeListener,
+ " expanded=%b selectionChanged=%b selected=%s"
+ " suppressed=%s unsupressed=%s shouldShowEducation=%b",
update.addedBubble != null ? update.addedBubble.getKey() : "null",
- update.removedBubbles.isEmpty(),
+ !update.removedBubbles.isEmpty(),
update.updatedBubble != null ? update.updatedBubble.getKey() : "null",
update.orderChanged, update.expandedChanged, update.expanded,
update.selectionChanged,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index eddd43f263d9..271fb9abce6a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -143,6 +143,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCurrentCornerRadius);
}
});
+ // Set a touch sink to ensure that clicks on the caption area do not propagate to the parent
+ setOnTouchListener((v, event) -> true);
}
@Override
@@ -245,12 +247,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
- int menuViewHeight = Math.min(mCaptionHeight, height);
- measureChild(mHandleView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight,
- MeasureSpec.getMode(heightMeasureSpec)));
-
if (mTaskView != null) {
+ int height = MeasureSpec.getSize(heightMeasureSpec);
measureChild(mTaskView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(height,
MeasureSpec.getMode(heightMeasureSpec)));
}
@@ -259,14 +257,11 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
- final int captionBottom = t + mCaptionHeight;
if (mTaskView != null) {
mTaskView.layout(l, t, r,
t + mTaskView.getMeasuredHeight());
mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0));
}
- // Handle draws on top of task view in the caption area.
- mHandleView.layout(l, t, r, captionBottom);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 62f2726ad9bd..78a41f759d96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -231,6 +231,7 @@ public class BubbleBarLayerView extends FrameLayout
// Touch delegate for the menu
BubbleBarHandleView view = mExpandedView.getHandleView();
view.getBoundsOnScreen(mHandleTouchBounds);
+ // Move top value up to ensure touch target is large enough
mHandleTouchBounds.top -= mPositioner.getBubblePaddingTop();
mHandleTouchDelegate = new TouchDelegate(mHandleTouchBounds,
mExpandedView.getHandleView());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 1959eb03a6b3..86cec02ab138 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -22,12 +22,7 @@ import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.os.Process.SYSTEM_UID;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
-import static android.util.RotationUtils.rotateBounds;
-import static android.util.RotationUtils.rotateInsets;
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -40,11 +35,9 @@ import android.graphics.Rect;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.DisplayMetrics;
-import android.util.Size;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
-import android.view.Gravity;
import android.view.InsetsState;
import android.view.Surface;
import android.view.WindowInsets;
@@ -226,25 +219,22 @@ public class DisplayLayout {
/**
* Apply a rotation to this layout and its parameters.
- * @param res
- * @param targetRotation
*/
- public void rotateTo(Resources res, @Surface.Rotation int targetRotation) {
- final int rotationDelta = (targetRotation - mRotation + 4) % 4;
- final boolean changeOrient = (rotationDelta % 2) != 0;
-
+ public void rotateTo(Resources res, @Surface.Rotation int toRotation) {
final int origWidth = mWidth;
final int origHeight = mHeight;
+ final int fromRotation = mRotation;
+ final int rotationDelta = (toRotation - fromRotation + 4) % 4;
+ final boolean changeOrient = (rotationDelta % 2) != 0;
- mRotation = targetRotation;
+ mRotation = toRotation;
if (changeOrient) {
mWidth = origHeight;
mHeight = origWidth;
}
- if (mCutout != null && !mCutout.isEmpty()) {
- mCutout = calculateDisplayCutoutForRotation(mCutout, rotationDelta, origWidth,
- origHeight);
+ if (mCutout != null) {
+ mCutout = mCutout.getRotated(origWidth, origHeight, fromRotation, toRotation);
}
recalcInsets(res);
@@ -398,96 +388,6 @@ public class DisplayLayout {
}
}
- /** Calculate the DisplayCutout for a particular display size/rotation. */
- public static DisplayCutout calculateDisplayCutoutForRotation(
- DisplayCutout cutout, int rotation, int displayWidth, int displayHeight) {
- if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
- return null;
- }
- if (rotation == ROTATION_0) {
- return computeSafeInsets(cutout, displayWidth, displayHeight);
- }
- final Insets waterfallInsets = rotateInsets(cutout.getWaterfallInsets(), rotation);
- final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- Rect[] cutoutRects = cutout.getBoundingRectsAll();
- final Rect[] newBounds = new Rect[cutoutRects.length];
- final Rect displayBounds = new Rect(0, 0, displayWidth, displayHeight);
- for (int i = 0; i < cutoutRects.length; ++i) {
- final Rect rect = new Rect(cutoutRects[i]);
- if (!rect.isEmpty()) {
- rotateBounds(rect, displayBounds, rotation);
- }
- newBounds[getBoundIndexFromRotation(i, rotation)] = rect;
- }
- final DisplayCutout.CutoutPathParserInfo info = cutout.getCutoutPathParserInfo();
- final DisplayCutout.CutoutPathParserInfo newInfo = new DisplayCutout.CutoutPathParserInfo(
- info.getDisplayWidth(), info.getDisplayHeight(), info.getPhysicalDisplayWidth(),
- info.getPhysicalDisplayHeight(), info.getDensity(), info.getCutoutSpec(), rotation,
- info.getScale(), info.getPhysicalPixelDisplaySizeRatio());
- return computeSafeInsets(
- DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo),
- rotated ? displayHeight : displayWidth,
- rotated ? displayWidth : displayHeight);
- }
-
- private static int getBoundIndexFromRotation(int index, int rotation) {
- return (index - rotation) < 0
- ? index - rotation + DisplayCutout.BOUNDS_POSITION_LENGTH
- : index - rotation;
- }
-
- /** Calculate safe insets. */
- public static DisplayCutout computeSafeInsets(DisplayCutout inner,
- int displayWidth, int displayHeight) {
- if (inner == DisplayCutout.NO_CUTOUT) {
- return null;
- }
-
- final Size displaySize = new Size(displayWidth, displayHeight);
- final Rect safeInsets = computeSafeInsets(displaySize, inner);
- return inner.replaceSafeInsets(safeInsets);
- }
-
- private static Rect computeSafeInsets(
- Size displaySize, DisplayCutout cutout) {
- if (displaySize.getWidth() == displaySize.getHeight()) {
- throw new UnsupportedOperationException("not implemented: display=" + displaySize
- + " cutout=" + cutout);
- }
-
- int leftInset = Math.max(cutout.getWaterfallInsets().left,
- findCutoutInsetForSide(displaySize, cutout.getBoundingRectLeft(), Gravity.LEFT));
- int topInset = Math.max(cutout.getWaterfallInsets().top,
- findCutoutInsetForSide(displaySize, cutout.getBoundingRectTop(), Gravity.TOP));
- int rightInset = Math.max(cutout.getWaterfallInsets().right,
- findCutoutInsetForSide(displaySize, cutout.getBoundingRectRight(), Gravity.RIGHT));
- int bottomInset = Math.max(cutout.getWaterfallInsets().bottom,
- findCutoutInsetForSide(displaySize, cutout.getBoundingRectBottom(),
- Gravity.BOTTOM));
-
- return new Rect(leftInset, topInset, rightInset, bottomInset);
- }
-
- private static int findCutoutInsetForSide(Size display, Rect boundingRect, int gravity) {
- if (boundingRect.isEmpty()) {
- return 0;
- }
-
- int inset = 0;
- switch (gravity) {
- case Gravity.TOP:
- return Math.max(inset, boundingRect.bottom);
- case Gravity.BOTTOM:
- return Math.max(inset, display.getHeight() - boundingRect.top);
- case Gravity.LEFT:
- return Math.max(inset, boundingRect.right);
- case Gravity.RIGHT:
- return Math.max(inset, display.getWidth() - boundingRect.left);
- default:
- throw new IllegalArgumentException("unknown gravity: " + gravity);
- }
- }
-
static boolean hasNavigationBar(DisplayInfo info, Context context, int displayId) {
if (displayId == Display.DEFAULT_DISPLAY) {
// Allow a system property to override this. Used by the emulator.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
index a9f687fc9b2d..6ffeb97f50fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
@@ -125,11 +125,15 @@ public class PipBoundsAlgorithm {
public Rect getEntryDestinationBoundsIgnoringKeepClearAreas() {
final PipBoundsState.PipReentryState reentryState = mPipBoundsState.getReentryState();
- final Rect destinationBounds = reentryState != null
- ? getDefaultBounds(reentryState.getSnapFraction(), reentryState.getSize())
- : getDefaultBounds();
+ final Rect destinationBounds = getDefaultBounds();
+ if (reentryState != null) {
+ final Size scaledBounds = new Size(
+ Math.round(mPipBoundsState.getMaxSize().x * reentryState.getBoundsScale()),
+ Math.round(mPipBoundsState.getMaxSize().y * reentryState.getBoundsScale()));
+ destinationBounds.set(getDefaultBounds(reentryState.getSnapFraction(), scaledBounds));
+ }
- final boolean useCurrentSize = reentryState != null && reentryState.getSize() != null;
+ final boolean useCurrentSize = reentryState != null;
Rect aspectRatioBounds = transformBoundsToAspectRatioIfValid(destinationBounds,
mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
useCurrentSize);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
index df589df8b227..b57e2d2c6ac2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
@@ -298,8 +298,8 @@ public class PipBoundsState {
}
/** Save the reentry state to restore to when re-entering PIP mode. */
- public void saveReentryState(Size size, float fraction) {
- mPipReentryState = new PipReentryState(size, fraction);
+ public void saveReentryState(float fraction) {
+ mPipReentryState = new PipReentryState(mBoundsScale, fraction);
}
/** Returns the saved reentry state. */
@@ -601,17 +601,16 @@ public class PipBoundsState {
public static final class PipReentryState {
private static final String TAG = PipReentryState.class.getSimpleName();
- private final @Nullable Size mSize;
private final float mSnapFraction;
+ private final float mBoundsScale;
- PipReentryState(@Nullable Size size, float snapFraction) {
- mSize = size;
+ PipReentryState(float boundsScale, float snapFraction) {
+ mBoundsScale = boundsScale;
mSnapFraction = snapFraction;
}
- @Nullable
- public Size getSize() {
- return mSize;
+ public float getBoundsScale() {
+ return mBoundsScale;
}
public float getSnapFraction() {
@@ -621,7 +620,7 @@ public class PipBoundsState {
void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
- pw.println(innerPrefix + "mSize=" + mSize);
+ pw.println(innerPrefix + "mBoundsScale=" + mBoundsScale);
pw.println(innerPrefix + "mSnapFraction=" + mSnapFraction);
}
}
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 4fe79c13e722..f757e1c88cb8 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
@@ -65,7 +65,7 @@ import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
import com.android.wm.shell.draganddrop.DragAndDropController;
-import com.android.wm.shell.draganddrop.UnhandledDragController;
+import com.android.wm.shell.draganddrop.GlobalDragListener;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler;
@@ -498,6 +498,7 @@ public abstract class WMShellModule {
ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ DragAndDropController dragAndDropController,
Transitions transitions,
EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
@@ -506,14 +507,15 @@ public abstract class WMShellModule {
@DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
LaunchAdjacentController launchAdjacentController,
RecentsTransitionHandler recentsTransitionHandler,
+ MultiInstanceHelper multiInstanceHelper,
@ShellMainThread ShellExecutor mainExecutor
) {
return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
- transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler,
- toggleResizeDesktopTaskTransitionHandler, dragToDesktopTransitionHandler,
- desktopModeTaskRepository, launchAdjacentController, recentsTransitionHandler,
- mainExecutor);
+ dragAndDropController, transitions, enterDesktopTransitionHandler,
+ exitDesktopTransitionHandler, toggleResizeDesktopTaskTransitionHandler,
+ dragToDesktopTransitionHandler, desktopModeTaskRepository, launchAdjacentController,
+ recentsTransitionHandler, multiInstanceHelper, mainExecutor);
}
@WMSingleton
@@ -562,10 +564,10 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
- static UnhandledDragController provideUnhandledDragController(
+ static GlobalDragListener provideGlobalDragListener(
IWindowManager wmService,
@ShellMainThread ShellExecutor mainExecutor) {
- return new UnhandledDragController(wmService, mainExecutor);
+ return new GlobalDragListener(wmService, mainExecutor);
}
@WMSingleton
@@ -577,9 +579,12 @@ public abstract class WMShellModule {
DisplayController displayController,
UiEventLogger uiEventLogger,
IconProvider iconProvider,
+ GlobalDragListener globalDragListener,
+ Transitions transitions,
@ShellMainThread ShellExecutor mainExecutor) {
- return new DragAndDropController(context, shellInit, shellController,
- shellCommandHandler, displayController, uiEventLogger, iconProvider, mainExecutor);
+ return new DragAndDropController(context, shellInit, shellController, shellCommandHandler,
+ displayController, uiEventLogger, iconProvider, globalDragListener, transitions,
+ mainExecutor);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 6250fc5820aa..405341803a46 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -19,6 +19,8 @@ package com.android.wm.shell.desktopmode;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FINAL_FREEFORM_SCALE;
@@ -28,11 +30,13 @@ import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.Region;
import android.util.DisplayMetrics;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
@@ -41,6 +45,8 @@ import android.view.WindowManager;
import android.view.WindowlessWindowManager;
import android.view.animation.DecelerateInterpolator;
+import androidx.annotation.VisibleForTesting;
+
import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -93,28 +99,114 @@ public class DesktopModeVisualIndicator {
/**
* Based on the coordinates of the current drag event, determine which indicator type we should
* display, including no visible indicator.
- * TODO(b/280828642): Update drag zones per starting windowing mode.
*/
- IndicatorType updateIndicatorType(PointF inputCoordinates) {
+ IndicatorType updateIndicatorType(PointF inputCoordinates, int windowingMode) {
final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId);
// If we are in freeform, we don't want a visible indicator in the "freeform" drag zone.
- IndicatorType result = mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
- ? IndicatorType.NO_INDICATOR : IndicatorType.TO_DESKTOP_INDICATOR;
- int transitionAreaHeight = mContext.getResources().getDimensionPixelSize(
- com.android.wm.shell.R.dimen.desktop_mode_transition_area_height);
- int transitionAreaWidth = mContext.getResources().getDimensionPixelSize(
+ IndicatorType result = IndicatorType.NO_INDICATOR;
+ final int transitionAreaWidth = mContext.getResources().getDimensionPixelSize(
com.android.wm.shell.R.dimen.desktop_mode_transition_area_width);
- if (inputCoordinates.y <= transitionAreaHeight) {
+ // Because drags in freeform use task position for indicator calculation, we need to
+ // account for the possibility of the task going off the top of the screen by captionHeight
+ final int captionHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_freeform_decor_caption_height);
+ final Region fullscreenRegion = calculateFullscreenRegion(layout, windowingMode,
+ captionHeight);
+ final Region splitLeftRegion = calculateSplitLeftRegion(layout, windowingMode,
+ transitionAreaWidth, captionHeight);
+ final Region splitRightRegion = calculateSplitRightRegion(layout, windowingMode,
+ transitionAreaWidth, captionHeight);
+ final Region toDesktopRegion = calculateToDesktopRegion(layout, windowingMode,
+ splitLeftRegion, splitRightRegion, fullscreenRegion);
+ if (fullscreenRegion.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
result = IndicatorType.TO_FULLSCREEN_INDICATOR;
- } else if (inputCoordinates.x <= transitionAreaWidth) {
+ }
+ if (splitLeftRegion.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
result = IndicatorType.TO_SPLIT_LEFT_INDICATOR;
- } else if (inputCoordinates.x >= layout.width() - transitionAreaWidth) {
+ }
+ if (splitRightRegion.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
result = IndicatorType.TO_SPLIT_RIGHT_INDICATOR;
}
+ if (toDesktopRegion.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
+ result = IndicatorType.TO_DESKTOP_INDICATOR;
+ }
transitionIndicator(result);
return result;
}
+ @VisibleForTesting
+ Region calculateFullscreenRegion(DisplayLayout layout,
+ @WindowConfiguration.WindowingMode int windowingMode, int captionHeight) {
+ final Region region = new Region();
+ int edgeTransitionHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_height);
+ // A thin, short Rect at the top of the screen.
+ if (windowingMode == WINDOWING_MODE_FREEFORM) {
+ int fromFreeformWidth = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_width);
+ int fromFreeformHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height);
+ region.union(new Rect((layout.width() / 2) - (fromFreeformWidth / 2),
+ -captionHeight,
+ (layout.width() / 2) + (fromFreeformWidth / 2),
+ fromFreeformHeight));
+ }
+ // A screen-wide, shorter Rect if the task is in fullscreen or split.
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+ region.union(new Rect(0,
+ -captionHeight,
+ layout.width(),
+ edgeTransitionHeight));
+ }
+ return region;
+ }
+
+ @VisibleForTesting
+ Region calculateToDesktopRegion(DisplayLayout layout,
+ @WindowConfiguration.WindowingMode int windowingMode,
+ Region splitLeftRegion, Region splitRightRegion,
+ Region toFullscreenRegion) {
+ final Region region = new Region();
+ // If in desktop, we need no region. Otherwise it's the same for all windowing modes.
+ if (windowingMode != WINDOWING_MODE_FREEFORM) {
+ region.union(new Rect(0, 0, layout.width(), layout.height()));
+ region.op(splitLeftRegion, Region.Op.DIFFERENCE);
+ region.op(splitRightRegion, Region.Op.DIFFERENCE);
+ region.op(toFullscreenRegion, Region.Op.DIFFERENCE);
+ }
+ return region;
+ }
+
+ @VisibleForTesting
+ Region calculateSplitLeftRegion(DisplayLayout layout,
+ @WindowConfiguration.WindowingMode int windowingMode,
+ int transitionEdgeWidth, int captionHeight) {
+ final Region region = new Region();
+ // In freeform, keep the top corners clear.
+ int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM
+ ? mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_split_from_desktop_height) :
+ -captionHeight;
+ region.union(new Rect(0, transitionHeight, transitionEdgeWidth, layout.height()));
+ return region;
+ }
+
+ @VisibleForTesting
+ Region calculateSplitRightRegion(DisplayLayout layout,
+ @WindowConfiguration.WindowingMode int windowingMode,
+ int transitionEdgeWidth, int captionHeight) {
+ final Region region = new Region();
+ // In freeform, keep the top corners clear.
+ int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM
+ ? mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_split_from_desktop_height) :
+ -captionHeight;
+ region.union(new Rect(layout.width() - transitionEdgeWidth, transitionHeight,
+ layout.width(), layout.height()));
+ return region;
+ }
+
/**
* Create a fullscreen indicator with no animation
*/
@@ -175,7 +267,6 @@ public class DesktopModeVisualIndicator {
mDisplayController.getDisplayLayout(mTaskInfo.displayId));
animator.start();
mCurrentType = IndicatorType.NO_INDICATOR;
-
}
/**
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 645ff0608608..98f9988eaabb 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
@@ -16,7 +16,10 @@
package com.android.wm.shell.desktopmode
+import android.app.ActivityManager
import android.app.ActivityManager.RunningTaskInfo
+import android.app.ActivityOptions
+import android.app.PendingIntent
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -25,6 +28,7 @@ import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.app.WindowConfiguration.WindowingMode
import android.content.Context
+import android.content.Intent
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
@@ -32,6 +36,7 @@ import android.graphics.Region
import android.os.IBinder
import android.os.SystemProperties
import android.util.DisplayMetrics.DENSITY_DEFAULT
+import android.view.Display.DEFAULT_DISPLAY
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_NONE
@@ -49,6 +54,8 @@ import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.ExecutorUtils
import com.android.wm.shell.common.ExternalInterfaceBinder
import com.android.wm.shell.common.LaunchAdjacentController
+import com.android.wm.shell.common.MultiInstanceHelper
+import com.android.wm.shell.common.MultiInstanceHelper.Companion.getComponent
import com.android.wm.shell.common.RemoteCallable
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SingleInstanceRemoteListener
@@ -59,7 +66,9 @@ import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOT
import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener
+import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.recents.RecentTasksController
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.splitscreen.SplitScreenController
@@ -76,6 +85,7 @@ import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener
import java.io.PrintWriter
import java.util.concurrent.Executor
import java.util.function.Consumer
+import java.util.function.Function
/** Handles moving tasks in and out of desktop */
class DesktopTasksController(
@@ -87,6 +97,7 @@ class DesktopTasksController(
private val shellTaskOrganizer: ShellTaskOrganizer,
private val syncQueue: SyncTransactionQueue,
private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val dragAndDropController: DragAndDropController,
private val transitions: Transitions,
private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler,
private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
@@ -96,8 +107,10 @@ class DesktopTasksController(
private val desktopModeTaskRepository: DesktopModeTaskRepository,
private val launchAdjacentController: LaunchAdjacentController,
private val recentsTransitionHandler: RecentsTransitionHandler,
+ private val multiInstanceHelper: MultiInstanceHelper,
@ShellMainThread private val mainExecutor: ShellExecutor
-) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler {
+) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler,
+ DragAndDropController.DragAndDropListener {
private val desktopMode: DesktopModeImpl
private var visualIndicator: DesktopModeVisualIndicator? = null
@@ -174,6 +187,7 @@ class DesktopTasksController(
}
}
)
+ dragAndDropController.addListener(this)
}
fun setOnTaskResizeAnimationListener(listener: OnTaskResizeAnimationListener) {
@@ -930,7 +944,7 @@ class DesktopTasksController(
}
// Then, update the indicator type.
val indicator = visualIndicator ?: return
- indicator.updateIndicatorType(PointF(inputX, taskTop))
+ indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode)
}
/**
@@ -1023,6 +1037,50 @@ class DesktopTasksController(
desktopModeTaskRepository.setExclusionRegionListener(listener, callbackExecutor)
}
+ override fun onUnhandledDrag(
+ launchIntent: PendingIntent,
+ dragSurface: SurfaceControl,
+ onFinishCallback: Consumer<Boolean>
+ ): Boolean {
+ // TODO(b/320797628): Pass through which display we are dropping onto
+ val activeTasks = desktopModeTaskRepository.getActiveTasks(DEFAULT_DISPLAY)
+ if (!activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) {
+ // Not currently in desktop mode, ignore the drop
+ return false
+ }
+
+ val launchComponent = getComponent(launchIntent)
+ if (!multiInstanceHelper.supportsMultiInstanceSplit(launchComponent)) {
+ // TODO(b/320797628): Should only return early if there is an existing running task, and
+ // notify the user as well. But for now, just ignore the drop.
+ KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "Dropped intent does not support multi-instance")
+ return false
+ }
+
+ // Start a new transition to launch the app
+ val opts = ActivityOptions.makeBasic().apply {
+ launchWindowingMode = WINDOWING_MODE_FREEFORM
+ pendingIntentLaunchFlags =
+ Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+ setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+ isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
+ }
+ val wct = WindowContainerTransaction()
+ wct.sendPendingIntent(launchIntent, null, opts.toBundle())
+ transitions.startTransition(TRANSIT_OPEN, wct, null /* handler */)
+
+ // Report that this is handled by the listener
+ onFinishCallback.accept(true)
+
+ // We've assumed responsibility of cleaning up the drag surface, so do that now
+ // TODO(b/320797628): Do an actual animation here for the drag surface
+ val t = SurfaceControl.Transaction()
+ t.remove(dragSurface)
+ t.apply()
+ return true
+ }
+
private fun dump(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
pw.println("${prefix}DesktopTasksController")
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 269c3699ac0a..1afbdf90eac0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -35,7 +35,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DRAG_AND_DROP;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
import android.content.ClipDescription;
import android.content.ComponentCallbacks2;
import android.content.Context;
@@ -51,6 +53,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
+import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
@@ -71,14 +74,18 @@ import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Handles the global drag and drop handling for the Shell.
*/
public class DragAndDropController implements RemoteCallable<DragAndDropController>,
+ GlobalDragListener.GlobalDragListenerCallback,
DisplayController.OnDisplaysChangedListener,
View.OnDragListener, ComponentCallbacks2 {
@@ -90,6 +97,8 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
private final DisplayController mDisplayController;
private final DragAndDropEventLogger mLogger;
private final IconProvider mIconProvider;
+ private final GlobalDragListener mGlobalDragListener;
+ private final Transitions mTransitions;
private SplitScreenController mSplitScreen;
private ShellExecutor mMainExecutor;
private ArrayList<DragAndDropListener> mListeners = new ArrayList<>();
@@ -97,12 +106,29 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
// Map of displayId -> per-display info
private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
+ // The current display if a drag is in progress
+ private int mActiveDragDisplay = -1;
+
/**
- * Listener called during drag events, currently just onDragStarted.
+ * Listener called during drag events.
*/
public interface DragAndDropListener {
/** Called when a drag has started. */
- void onDragStarted();
+ default void onDragStarted() {}
+
+ /** Called when a drag has ended. */
+ default void onDragEnded() {}
+
+ /**
+ * Called when an unhandled drag has occurred. The impl must return true if it decides to
+ * handled the unhandled drag, and it must also call `onFinishCallback` to complete the
+ * drag.
+ */
+ default boolean onUnhandledDrag(@NonNull PendingIntent launchIntent,
+ @NonNull SurfaceControl dragSurface,
+ @NonNull Consumer<Boolean> onFinishCallback) {
+ return false;
+ }
}
public DragAndDropController(Context context,
@@ -112,6 +138,8 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
DisplayController displayController,
UiEventLogger uiEventLogger,
IconProvider iconProvider,
+ GlobalDragListener globalDragListener,
+ Transitions transitions,
ShellExecutor mainExecutor) {
mContext = context;
mShellController = shellController;
@@ -119,6 +147,8 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
mDisplayController = displayController;
mLogger = new DragAndDropEventLogger(uiEventLogger);
mIconProvider = iconProvider;
+ mGlobalDragListener = globalDragListener;
+ mTransitions = transitions;
mMainExecutor = mainExecutor;
shellInit.addInitCallback(this::onInit, this);
}
@@ -136,6 +166,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
mShellController.addExternalInterface(KEY_EXTRA_SHELL_DRAG_AND_DROP,
this::createExternalInterface, this);
mShellCommandHandler.addDumpCallback(this::dump, this);
+ mGlobalDragListener.setListener(this);
}
private ExternalInterfaceBinder createExternalInterface() {
@@ -169,10 +200,18 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
mListeners.remove(listener);
}
- private void notifyDragStarted() {
+ /**
+ * Notifies all listeners and returns whether any listener handled the callback.
+ */
+ private boolean notifyListeners(Function<DragAndDropListener, Boolean> callback) {
for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).onDragStarted();
+ boolean handled = callback.apply(mListeners.get(i));
+ if (handled) {
+ // Return once the callback reports it has handled it
+ return true;
+ }
}
+ return false;
}
@Override
@@ -258,6 +297,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
}
if (event.getAction() == ACTION_DRAG_STARTED) {
+ mActiveDragDisplay = displayId;
pd.isHandlingDrag = DragUtils.canHandleDrag(event);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"Clip description: handlingDrag=%b itemCount=%d mimeTypes=%s",
@@ -283,7 +323,11 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
pd.dragSession.update();
pd.dragLayout.prepare(pd.dragSession, loggerSessionId);
setDropTargetWindowVisibility(pd, View.VISIBLE);
- notifyDragStarted();
+ notifyListeners(l -> {
+ l.onDragStarted();
+ // Return false to continue dispatch to next listener
+ return false;
+ });
break;
case ACTION_DRAG_ENTERED:
pd.dragLayout.show();
@@ -317,11 +361,43 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
});
}
mLogger.logEnd();
+ mActiveDragDisplay = -1;
+ notifyListeners(l -> {
+ l.onDragEnded();
+ // Return false to continue dispatch to next listener
+ return false;
+ });
break;
}
return true;
}
+ @Override
+ public void onCrossWindowDrop(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
+ // Bring the task forward when an item is dropped on it
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reorder(taskInfo.token, true /* onTop */);
+ mTransitions.startTransition(WindowManager.TRANSIT_TO_FRONT, wct, null);
+ }
+
+ @Override
+ public void onUnhandledDrop(@NonNull DragEvent dragEvent,
+ @NonNull Consumer<Boolean> onFinishCallback) {
+ final PendingIntent launchIntent = DragUtils.getLaunchIntent(dragEvent);
+ if (launchIntent == null) {
+ // No intent to launch, report that this is unhandled by the listener
+ onFinishCallback.accept(false);
+ return;
+ }
+
+ final boolean handled = notifyListeners(
+ l -> l.onUnhandledDrag(launchIntent, dragEvent.getDragSurface(), onFinishCallback));
+ if (!handled) {
+ // Nobody handled this, we still have to notify WM
+ onFinishCallback.accept(false);
+ }
+ }
+
/**
* Handles dropping on the drop target.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
index 7c0883d2538f..f7bcc9477aa1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
@@ -20,9 +20,14 @@ import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+import android.app.PendingIntent;
+import android.content.ClipData;
import android.content.ClipDescription;
import android.view.DragEvent;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
/** Collection of utility classes for handling drag and drop. */
public class DragUtils {
private static final String TAG = "DragUtils";
@@ -45,6 +50,31 @@ public class DragUtils {
}
/**
+ * Returns a launchable intent in the given `DragEvent` or `null` if there is none.
+ */
+ @Nullable
+ public static PendingIntent getLaunchIntent(@NonNull DragEvent dragEvent) {
+ return getLaunchIntent(dragEvent.getClipData());
+ }
+
+ /**
+ * Returns a launchable intent in the given `ClipData` or `null` if there is none.
+ */
+ @Nullable
+ public static PendingIntent getLaunchIntent(@NonNull ClipData data) {
+ for (int i = 0; i < data.getItemCount(); i++) {
+ final ClipData.Item item = data.getItemAt(i);
+ if (item.getIntentSender() != null) {
+ final PendingIntent intent = new PendingIntent(item.getIntentSender().getTarget());
+ if (intent != null && intent.isActivity()) {
+ return intent;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
* Returns a list of the mime types provided in the clip description.
*/
public static String getMimeTypesConcatenated(ClipDescription description) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/UnhandledDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
index ccf48d0de9ed..8826141fb406 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/UnhandledDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
@@ -15,12 +15,13 @@
*/
package com.android.wm.shell.draganddrop
+import android.app.ActivityManager
import android.os.RemoteException
import android.util.Log
import android.view.DragEvent
import android.view.IWindowManager
+import android.window.IGlobalDragListener
import android.window.IUnhandledDragCallback
-import android.window.IUnhandledDragListener
import androidx.annotation.VisibleForTesting
import com.android.internal.protolog.common.ProtoLog
import com.android.wm.shell.common.ShellExecutor
@@ -29,26 +30,38 @@ import java.util.function.Consumer
/**
* Manages the listener and callbacks for unhandled global drags.
+ * This is only used by DragAndDropController and should not be used directly by other classes.
*/
-class UnhandledDragController(
- val wmService: IWindowManager,
- mainExecutor: ShellExecutor
+class GlobalDragListener(
+ private val wmService: IWindowManager,
+ private val mainExecutor: ShellExecutor
) {
- private var callback: UnhandledDragAndDropCallback? = null
+ private var callback: GlobalDragListenerCallback? = null
+
+ private val globalDragListener: IGlobalDragListener =
+ object : IGlobalDragListener.Stub() {
+ override fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) {
+ mainExecutor.execute() {
+ this@GlobalDragListener.onCrossWindowDrop(taskInfo)
+ }
+ }
- private val unhandledDragListener: IUnhandledDragListener =
- object : IUnhandledDragListener.Stub() {
override fun onUnhandledDrop(event: DragEvent, callback: IUnhandledDragCallback) {
mainExecutor.execute() {
- this@UnhandledDragController.onUnhandledDrop(event, callback)
+ this@GlobalDragListener.onUnhandledDrop(event, callback)
}
}
}
/**
- * Listener called when an unhandled drag is started.
+ * Callbacks for global drag events.
*/
- interface UnhandledDragAndDropCallback {
+ interface GlobalDragListenerCallback {
+ /**
+ * Called when a global drag is successfully handled by another window.
+ */
+ fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) {}
+
/**
* Called when a global drag is unhandled (ie. dropped outside of all visible windows, or
* dropped on a window that does not want to handle it).
@@ -62,7 +75,7 @@ class UnhandledDragController(
/**
* Sets a listener for callbacks when an unhandled drag happens.
*/
- fun setListener(listener: UnhandledDragAndDropCallback?) {
+ fun setListener(listener: GlobalDragListenerCallback?) {
val updateWm = (callback == null && listener != null)
|| (callback != null && listener == null)
callback = listener
@@ -71,8 +84,8 @@ class UnhandledDragController(
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"%s unhandled drag listener",
if (callback != null) "Registering" else "Unregistering")
- wmService.setUnhandledDragListener(
- if (callback != null) unhandledDragListener else null)
+ wmService.setGlobalDragListener(
+ if (callback != null) globalDragListener else null)
} catch (e: RemoteException) {
Log.e(TAG, "Failed to set unhandled drag listener")
}
@@ -80,11 +93,19 @@ class UnhandledDragController(
}
@VisibleForTesting
+ fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "onCrossWindowDrop: %s", taskInfo)
+ callback?.onCrossWindowDrop(taskInfo)
+ }
+
+ @VisibleForTesting
fun onUnhandledDrop(dragEvent: DragEvent, wmCallback: IUnhandledDragCallback) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"onUnhandledDrop: %s", dragEvent)
if (callback == null) {
wmCallback.notifyUnhandledDropComplete(false)
+ return
}
callback?.onUnhandledDrop(dragEvent) {
@@ -95,6 +116,6 @@ class UnhandledDragController(
}
companion object {
- private val TAG = UnhandledDragController::class.java.simpleName
+ private val TAG = GlobalDragListener::class.java.simpleName
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index ef32f6f5dbe1..c7daf561f682 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -1996,6 +1996,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
pw.println(innerPrefix + "mToken=" + mToken
+ " binder=" + (mToken != null ? mToken.asBinder() : null));
pw.println(innerPrefix + "mLeash=" + mLeash);
+ pw.println(innerPrefix + "mPipOverlay=" + mPipOverlay);
pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState());
pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
mPipTransitionController.dump(pw, innerPrefix);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index d17317522694..32442f740a52 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -191,7 +191,7 @@ public abstract class PipTransitionController implements Transitions.TransitionH
try {
ActivityTaskManager.getService().onPictureInPictureUiStateChanged(
new PictureInPictureUiState.Builder()
- .setEnteringPip(true)
+ .setTransitioningToPip(true)
.build());
} catch (RemoteException | IllegalStateException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -210,7 +210,7 @@ public abstract class PipTransitionController implements Transitions.TransitionH
try {
ActivityTaskManager.getService().onPictureInPictureUiStateChanged(
new PictureInPictureUiState.Builder()
- .setEnteringPip(false)
+ .setTransitioningToPip(false)
.build());
} catch (RemoteException | IllegalStateException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
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 4b12134cb377..46840773cfc6 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
@@ -48,7 +48,6 @@ import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Pair;
-import android.util.Size;
import android.view.DisplayInfo;
import android.view.InsetsState;
import android.view.SurfaceControl;
@@ -1042,22 +1041,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
/** Save the state to restore to on re-entry. */
public void saveReentryState(Rect pipBounds) {
float snapFraction = mPipBoundsAlgorithm.getSnapFraction(pipBounds);
-
- if (!mPipBoundsState.hasUserResizedPip()) {
- mPipBoundsState.saveReentryState(null /* bounds */, snapFraction);
- return;
- }
-
- Size reentrySize = new Size(pipBounds.width(), pipBounds.height());
-
- // TODO: b/279937014 Investigate why userResizeBounds are empty with shell transitions on
- // fallback to using the userResizeBounds if userResizeBounds are not empty
- if (!mTouchHandler.getUserResizeBounds().isEmpty()) {
- Rect userResizeBounds = mTouchHandler.getUserResizeBounds();
- reentrySize = new Size(userResizeBounds.width(), userResizeBounds.height());
- }
-
- mPipBoundsState.saveReentryState(reentrySize, snapFraction);
+ mPipBoundsState.saveReentryState(snapFraction);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 5e79681e060b..b8a0f6703b97 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -493,11 +493,9 @@ public class Transitions implements RemoteCallable<Transitions>,
final SurfaceControl leash = change.getLeash();
final int mode = info.getChanges().get(i).getMode();
- if (mode == TRANSIT_TO_FRONT
- && ((change.getStartAbsBounds().height() != change.getEndAbsBounds().height()
- || change.getStartAbsBounds().width() != change.getEndAbsBounds().width()))) {
- // When the window is moved to front with a different size, make sure the crop is
- // updated to prevent it from using the old crop.
+ if (mode == TRANSIT_TO_FRONT) {
+ // When the window is moved to front, make sure the crop is updated to prevent it
+ // from using the old crop.
t.setWindowCrop(leash, change.getEndAbsBounds().width(),
change.getEndAbsBounds().height());
}
@@ -1170,7 +1168,11 @@ public class Transitions implements RemoteCallable<Transitions>,
mPendingTransitions.add(0, active);
}
- /** Start a new transition directly. */
+ /**
+ * Start a new transition directly.
+ * @param handler if null, the transition will be dispatched to the registered set of transition
+ * handlers to be handled
+ */
public IBinder startTransition(@WindowManager.TransitionType int type,
@NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition "
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 c713a2ed24b0..57e964f15d50 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
@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.WindowInsets.Type.statusBars;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -429,8 +430,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
final long eventDuration = e.getEventTime() - e.getDownTime();
- final boolean shouldLongClick = id == R.id.maximize_window && !mIsDragging
- && !mHasLongClicked && eventDuration >= ViewConfiguration.getLongPressTimeout();
+ final boolean isTouchScreen =
+ (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
+ final boolean shouldLongClick = isTouchScreen && id == R.id.maximize_window
+ && !mIsDragging && !mHasLongClicked
+ && eventDuration >= ViewConfiguration.getLongPressTimeout();
if (shouldLongClick) {
v.performLongClick();
mHasLongClicked = true;
@@ -728,7 +732,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mTransitionDragActive = false;
final int statusBarHeight = getStatusBarHeight(
relevantDecor.mTaskInfo.displayId);
- if (ev.getY() > 2 * statusBarHeight) {
+ if (ev.getRawY() > 2 * statusBarHeight) {
if (DesktopModeStatus.isEnabled()) {
animateToDesktop(relevantDecor, ev);
}
@@ -753,10 +757,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDesktopTasksController.ifPresent(
c -> c.updateVisualIndicator(
relevantDecor.mTaskInfo,
- relevantDecor.mTaskSurface, ev.getX(), ev.getY()));
+ relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY()));
final int statusBarHeight = getStatusBarHeight(
relevantDecor.mTaskInfo.displayId);
- if (ev.getY() > statusBarHeight) {
+ if (ev.getRawY() > statusBarHeight) {
if (mMoveToDesktopAnimator == null) {
mMoveToDesktopAnimator = new MoveToDesktopAnimator(
mContext, mDragToDesktopAnimationStartBounds,
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
new file mode 100644
index 000000000000..8c0a524cda1e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test auto entering pip using a source rect hint.
+ *
+ * To run this test: `atest AutoEnterPipWithSourceRectHintTest`
+ *
+ * Actions:
+ * ```
+ * Launch an app in full screen
+ * Select "Auto-enter PiP" radio button
+ * Press "Set SourceRectHint" to create a temporary view that is used as the source rect hint
+ * Press Home button or swipe up to go Home and put [pipApp] in pip mode
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. All assertions are inherited from [AutoEnterPipOnGoToHomeTest]
+ * 2. Part of the test setup occurs automatically via
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ * ```
+ */
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class AutoEnterPipWithSourceRectHintTest(flicker: LegacyFlickerTest) :
+ AutoEnterPipOnGoToHomeTest(flicker) {
+ override val defaultEnterPip: FlickerBuilder.() -> Unit = {
+ setup {
+ pipApp.launchViaIntent(wmHelper)
+ pipApp.setSourceRectHint()
+ pipApp.enableAutoEnterForPipActivity()
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun pipOverlayNotShown() {
+ val overlay = ComponentNameMatcher.PIP_CONTENT_OVERLAY
+ flicker.assertLayers {
+ this.notContains(overlay)
+ }
+ }
+ @Presubmit
+ @Test
+ override fun pipOverlayLayerAppearThenDisappear() {
+ // we don't use overlay when entering with sourceRectHint
+ }
+
+ @Presubmit
+ @Test
+ override fun pipLayerOrOverlayRemainInsideVisibleBounds() {
+ // TODO (b/323511194): Looks like there is some bounciness with pip when using
+ // auto enter and sourceRectHint that causes the app to move outside of the display
+ // bounds during the transition.
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
new file mode 100644
index 000000000000..f8ce4ee8e1ce
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.graphics.Rect
+import android.graphics.Region
+import android.testing.AndroidTestingRunner
+import android.view.SurfaceControl
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.R
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopModeVisualIndicatorTest : ShellTestCase() {
+ @Mock private lateinit var taskInfo: RunningTaskInfo
+ @Mock private lateinit var syncQueue: SyncTransactionQueue
+ @Mock private lateinit var displayController: DisplayController
+ @Mock private lateinit var taskSurface: SurfaceControl
+ @Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock private lateinit var displayLayout: DisplayLayout
+
+ private lateinit var visualIndicator: DesktopModeVisualIndicator
+
+ @Before
+ fun setUp() {
+ visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo, displayController,
+ context, taskSurface, taskDisplayAreaOrganizer)
+ whenever(displayLayout.width()).thenReturn(DISPLAY_BOUNDS.width())
+ whenever(displayLayout.height()).thenReturn(DISPLAY_BOUNDS.height())
+ }
+
+ @Test
+ fun testFullscreenRegionCalculation() {
+ val transitionHeight = context.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_transition_area_height)
+ val fromFreeformWidth = mContext.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_fullscreen_from_desktop_width
+ )
+ val fromFreeformHeight = mContext.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_fullscreen_from_desktop_height
+ )
+ var testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
+ WINDOWING_MODE_FULLSCREEN, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, transitionHeight))
+ testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
+ WINDOWING_MODE_FREEFORM, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(
+ DISPLAY_BOUNDS.width() / 2 - fromFreeformWidth / 2,
+ -50,
+ DISPLAY_BOUNDS.width() / 2 + fromFreeformWidth / 2,
+ fromFreeformHeight))
+ testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
+ WINDOWING_MODE_MULTI_WINDOW, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, transitionHeight))
+ }
+
+ @Test
+ fun testSplitLeftRegionCalculation() {
+ val transitionHeight = context.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_split_from_desktop_height)
+ var testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
+ WINDOWING_MODE_FULLSCREEN, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
+ testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
+ WINDOWING_MODE_FREEFORM, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(0, transitionHeight, 32, 1600))
+ testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
+ WINDOWING_MODE_MULTI_WINDOW, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
+ }
+
+ @Test
+ fun testSplitRightRegionCalculation() {
+ val transitionHeight = context.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_split_from_desktop_height)
+ var testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
+ WINDOWING_MODE_FULLSCREEN, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
+ testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
+ WINDOWING_MODE_FREEFORM, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(2368, transitionHeight, 2400, 1600))
+ testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
+ WINDOWING_MODE_MULTI_WINDOW, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
+ }
+
+ @Test
+ fun testToDesktopRegionCalculation() {
+ val fullscreenRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
+ WINDOWING_MODE_FULLSCREEN, CAPTION_HEIGHT)
+ val splitLeftRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
+ WINDOWING_MODE_FULLSCREEN, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ val splitRightRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
+ WINDOWING_MODE_FULLSCREEN, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ val desktopRegion = visualIndicator.calculateToDesktopRegion(displayLayout,
+ WINDOWING_MODE_FULLSCREEN, splitLeftRegion, splitRightRegion, fullscreenRegion)
+ var testRegion = Region()
+ testRegion.union(DISPLAY_BOUNDS)
+ testRegion.op(splitLeftRegion, Region.Op.DIFFERENCE)
+ testRegion.op(splitRightRegion, Region.Op.DIFFERENCE)
+ testRegion.op(fullscreenRegion, Region.Op.DIFFERENCE)
+ assertThat(desktopRegion).isEqualTo(testRegion)
+ }
+
+ companion object {
+ private const val TRANSITION_AREA_WIDTH = 32
+ private const val CAPTION_HEIGHT = 50
+ private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
+ }
+} \ No newline at end of file
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 cb64c52444ac..383621beca22 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -48,12 +48,14 @@ import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.LaunchAdjacentController
+import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask
+import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.splitscreen.SplitScreenController
@@ -106,6 +108,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration
@Mock lateinit var splitScreenController: SplitScreenController
@Mock lateinit var recentsTransitionHandler: RecentsTransitionHandler
+ @Mock lateinit var dragAndDropController: DragAndDropController
+ @Mock lateinit var multiInstanceHelper: MultiInstanceHelper
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var controller: DesktopTasksController
@@ -148,6 +152,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
shellTaskOrganizer,
syncQueue,
rootTaskDisplayAreaOrganizer,
+ dragAndDropController,
transitions,
enterDesktopTransitionHandler,
exitDesktopTransitionHandler,
@@ -156,6 +161,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
desktopModeTaskRepository,
launchAdjacentController,
recentsTransitionHandler,
+ multiInstanceHelper,
shellExecutor
)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
index 54f36f61859d..a64ebd301c00 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
@@ -45,12 +45,14 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
import org.junit.Test;
@@ -84,7 +86,9 @@ public class DragAndDropControllerTest extends ShellTestCase {
@Mock
private ShellExecutor mMainExecutor;
@Mock
- private WindowManager mWindowManager;
+ private Transitions mTransitions;
+ @Mock
+ private GlobalDragListener mGlobalDragListener;
private DragAndDropController mController;
@@ -93,7 +97,7 @@ public class DragAndDropControllerTest extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mController = new DragAndDropController(mContext, mShellInit, mShellController,
mShellCommandHandler, mDisplayController, mUiEventLogger, mIconProvider,
- mMainExecutor);
+ mGlobalDragListener, mTransitions, mMainExecutor);
mController.onInit();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/UnhandledDragControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/GlobalDragListenerTest.kt
index 522f05233f3a..e731b06c0947 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/UnhandledDragControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/GlobalDragListenerTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.draganddrop
+import android.app.ActivityManager.RunningTaskInfo
import android.os.RemoteException
import android.view.DragEvent
import android.view.DragEvent.ACTION_DROP
@@ -25,19 +26,17 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.draganddrop.UnhandledDragController.UnhandledDragAndDropCallback
+import com.android.wm.shell.draganddrop.GlobalDragListener.GlobalDragListenerCallback
import java.util.function.Consumer
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
-import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
/**
* Tests for the unhandled drag controller.
@@ -45,35 +44,31 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
class UnhandledDragControllerTest : ShellTestCase() {
- @Mock
- private lateinit var mIWindowManager: IWindowManager
+ private val mIWindowManager = mock<IWindowManager>()
+ private val mMainExecutor = mock<ShellExecutor>()
- @Mock
- private lateinit var mMainExecutor: ShellExecutor
-
- private lateinit var mController: UnhandledDragController
+ private lateinit var mController: GlobalDragListener
@Before
@Throws(RemoteException::class)
fun setUp() {
- MockitoAnnotations.initMocks(this)
- mController = UnhandledDragController(mIWindowManager, mMainExecutor)
+ mController = GlobalDragListener(mIWindowManager, mMainExecutor)
}
@Test
fun setListener_registersUnregistersWithWM() {
- mController.setListener(object : UnhandledDragAndDropCallback {})
- mController.setListener(object : UnhandledDragAndDropCallback {})
- mController.setListener(object : UnhandledDragAndDropCallback {})
+ mController.setListener(object : GlobalDragListenerCallback {})
+ mController.setListener(object : GlobalDragListenerCallback {})
+ mController.setListener(object : GlobalDragListenerCallback {})
verify(mIWindowManager, Mockito.times(1))
- .setUnhandledDragListener(ArgumentMatchers.any())
+ .setGlobalDragListener(ArgumentMatchers.any())
reset(mIWindowManager)
mController.setListener(null)
mController.setListener(null)
mController.setListener(null)
verify(mIWindowManager, Mockito.times(1))
- .setUnhandledDragListener(ArgumentMatchers.isNull())
+ .setGlobalDragListener(ArgumentMatchers.isNull())
}
@Test
@@ -81,7 +76,7 @@ class UnhandledDragControllerTest : ShellTestCase() {
// Simulate an unhandled drop
val dropEvent = DragEvent.obtain(ACTION_DROP, 0f, 0f, 0f, 0f, null, null, null,
null, null, false)
- val wmCallback = mock(IUnhandledDragCallback::class.java)
+ val wmCallback = mock<IUnhandledDragCallback>()
mController.onUnhandledDrop(dropEvent, wmCallback)
verify(wmCallback).notifyUnhandledDropComplete(ArgumentMatchers.eq(false))
@@ -92,7 +87,7 @@ class UnhandledDragControllerTest : ShellTestCase() {
val lastDragEvent = arrayOfNulls<DragEvent>(1)
// Set a listener to listen for unhandled drops
- mController.setListener(object : UnhandledDragAndDropCallback {
+ mController.setListener(object : GlobalDragListenerCallback {
override fun onUnhandledDrop(dragEvent: DragEvent,
onFinishedCallback: Consumer<Boolean>) {
lastDragEvent[0] = dragEvent
@@ -102,14 +97,31 @@ class UnhandledDragControllerTest : ShellTestCase() {
})
// Simulate an unhandled drop
- val dragSurface = mock(SurfaceControl::class.java)
+ val dragSurface = mock<SurfaceControl>()
val dropEvent = DragEvent.obtain(ACTION_DROP, 0f, 0f, 0f, 0f, null, null, null,
dragSurface, null, false)
- val wmCallback = mock(IUnhandledDragCallback::class.java)
+ val wmCallback = mock<IUnhandledDragCallback>()
mController.onUnhandledDrop(dropEvent, wmCallback)
verify(wmCallback).notifyUnhandledDropComplete(ArgumentMatchers.eq(true))
verify(dragSurface).release()
assertEquals(lastDragEvent.get(0), dropEvent)
}
+
+ @Test
+ fun onCrossWindowDrop() {
+ val lastTaskInfo = arrayOfNulls<RunningTaskInfo>(1)
+
+ // Set a listener to listen for unhandled drops
+ mController.setListener(object : GlobalDragListenerCallback {
+ override fun onCrossWindowDrop(taskInfo: RunningTaskInfo) {
+ lastTaskInfo[0] = taskInfo
+ }
+ })
+
+ // Simulate a cross-window drop
+ val taskInfo = mock<RunningTaskInfo>()
+ mController.onCrossWindowDrop(taskInfo)
+ assertEquals(lastTaskInfo.get(0), taskInfo)
+ }
}
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 46259a8b177f..080b0ae006ea 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
@@ -354,14 +354,17 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
}
@Test
- public void getEntryDestinationBounds_reentryStateExists_restoreLastSize() {
+ public void getEntryDestinationBounds_reentryStateExists_restoreProportionalSize() {
mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+ final Size maxSize = mSizeSpecSource.getMaxSize(DEFAULT_ASPECT_RATIO);
+ mPipBoundsState.setMaxSize(maxSize.getWidth(), maxSize.getHeight());
final Rect reentryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
reentryBounds.scale(1.25f);
+ mPipBoundsState.setBounds(reentryBounds); // this updates the bounds scale used in reentry
+
final float reentrySnapFraction = mPipBoundsAlgorithm.getSnapFraction(reentryBounds);
- mPipBoundsState.saveReentryState(
- new Size(reentryBounds.width(), reentryBounds.height()), reentrySnapFraction);
+ mPipBoundsState.saveReentryState(reentrySnapFraction);
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
assertEquals(reentryBounds.width(), destinationBounds.width());
@@ -375,8 +378,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
reentryBounds.offset(0, -100);
final float reentrySnapFraction = mPipBoundsAlgorithm.getSnapFraction(reentryBounds);
- mPipBoundsState.saveReentryState(
- new Size(reentryBounds.width(), reentryBounds.height()), reentrySnapFraction);
+ mPipBoundsState.saveReentryState(reentrySnapFraction);
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index db98abbbcbf1..304da75f870c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -114,22 +114,19 @@ public class PipBoundsStateTest extends ShellTestCase {
@Test
public void testSetReentryState() {
- final Size size = new Size(100, 100);
final float snapFraction = 0.5f;
- mPipBoundsState.saveReentryState(size, snapFraction);
+ mPipBoundsState.saveReentryState(snapFraction);
final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState();
- assertEquals(size, state.getSize());
assertEquals(snapFraction, state.getSnapFraction(), 0.01);
}
@Test
public void testClearReentryState() {
- final Size size = new Size(100, 100);
final float snapFraction = 0.5f;
- mPipBoundsState.saveReentryState(size, snapFraction);
+ mPipBoundsState.saveReentryState(snapFraction);
mPipBoundsState.clearReentryState();
assertNull(mPipBoundsState.getReentryState());
@@ -138,20 +135,19 @@ public class PipBoundsStateTest extends ShellTestCase {
@Test
public void testSetLastPipComponentName_notChanged_doesNotClearReentryState() {
mPipBoundsState.setLastPipComponentName(mTestComponentName1);
- mPipBoundsState.saveReentryState(DEFAULT_SIZE, DEFAULT_SNAP_FRACTION);
+ mPipBoundsState.saveReentryState(DEFAULT_SNAP_FRACTION);
mPipBoundsState.setLastPipComponentName(mTestComponentName1);
final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState();
assertNotNull(state);
- assertEquals(DEFAULT_SIZE, state.getSize());
assertEquals(DEFAULT_SNAP_FRACTION, state.getSnapFraction(), 0.01);
}
@Test
public void testSetLastPipComponentName_changed_clearReentryState() {
mPipBoundsState.setLastPipComponentName(mTestComponentName1);
- mPipBoundsState.saveReentryState(DEFAULT_SIZE, DEFAULT_SNAP_FRACTION);
+ mPipBoundsState.saveReentryState(DEFAULT_SNAP_FRACTION);
mPipBoundsState.setLastPipComponentName(mTestComponentName2);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 4eb519334e12..5d968d3360ab 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -45,7 +45,6 @@ import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.Size;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -256,40 +255,13 @@ public class PipControllerTest extends ShellTestCase {
}
@Test
- public void saveReentryState_noUserResize_doesNotSaveSize() {
+ public void saveReentryState_savesPipBoundsState() {
final Rect bounds = new Rect(0, 0, 10, 10);
when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f);
- when(mMockPipBoundsState.hasUserResizedPip()).thenReturn(false);
mPipController.saveReentryState(bounds);
- verify(mMockPipBoundsState).saveReentryState(null, 1.0f);
- }
-
- @Test
- public void saveReentryState_nonEmptyUserResizeBounds_savesSize() {
- final Rect bounds = new Rect(0, 0, 10, 10);
- final Rect resizedBounds = new Rect(0, 0, 30, 30);
- when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f);
- when(mMockPipTouchHandler.getUserResizeBounds()).thenReturn(resizedBounds);
- when(mMockPipBoundsState.hasUserResizedPip()).thenReturn(true);
-
- mPipController.saveReentryState(bounds);
-
- verify(mMockPipBoundsState).saveReentryState(new Size(30, 30), 1.0f);
- }
-
- @Test
- public void saveReentryState_emptyUserResizeBounds_savesSize() {
- final Rect bounds = new Rect(0, 0, 10, 10);
- final Rect resizedBounds = new Rect(0, 0, 0, 0);
- when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f);
- when(mMockPipTouchHandler.getUserResizeBounds()).thenReturn(resizedBounds);
- when(mMockPipBoundsState.hasUserResizedPip()).thenReturn(true);
-
- mPipController.saveReentryState(bounds);
-
- verify(mMockPipBoundsState).saveReentryState(new Size(10, 10), 1.0f);
+ verify(mMockPipBoundsState).saveReentryState(1.0f);
}
@Test
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index e4f3e2defb25..abd84de7da3c 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -155,6 +155,7 @@ cc_defaults {
host: {
static_libs: [
"libandroidfw",
+ "libhostgraphics",
"libutils",
],
},
@@ -501,6 +502,17 @@ cc_library_headers {
],
header_libs: ["android_graphics_jni_headers"],
export_header_lib_headers: ["android_graphics_jni_headers"],
+ target: {
+ android: {
+ export_include_dirs: ["platform/android"],
+ },
+ host: {
+ export_include_dirs: ["platform/host"],
+ },
+ windows: {
+ enabled: true,
+ },
+ },
}
cc_defaults {
@@ -538,6 +550,7 @@ cc_defaults {
"utils/Blur.cpp",
"utils/Color.cpp",
"utils/LinearAllocator.cpp",
+ "utils/StringUtils.cpp",
"utils/TypefaceUtils.cpp",
"utils/VectorDrawableUtils.cpp",
"AnimationContext.cpp",
@@ -552,6 +565,7 @@ cc_defaults {
"Mesh.cpp",
"MemoryPolicy.cpp",
"PathParser.cpp",
+ "ProfileData.cpp",
"Properties.cpp",
"PropertyValuesAnimatorSet.cpp",
"PropertyValuesHolder.cpp",
@@ -569,12 +583,13 @@ cc_defaults {
export_proto_headers: true,
},
+ header_libs: ["libandroid_headers_private"],
+
target: {
android: {
- header_libs: [
- "libandroid_headers_private",
- "libtonemap_headers",
- ],
+ header_libs: ["libtonemap_headers"],
+
+ local_include_dirs: ["platform/android"],
srcs: [
"hwui/AnimatedImageThread.cpp",
@@ -605,7 +620,6 @@ cc_defaults {
"thread/CommonPool.cpp",
"utils/GLUtils.cpp",
"utils/NdkUtils.cpp",
- "utils/StringUtils.cpp",
"AutoBackendTextureRelease.cpp",
"DeferredLayerUpdater.cpp",
"DeviceInfo.cpp",
@@ -617,7 +631,6 @@ cc_defaults {
"FrameMetricsReporter.cpp",
"Layer.cpp",
"LayerUpdateQueue.cpp",
- "ProfileData.cpp",
"ProfileDataContainer.cpp",
"Readback.cpp",
"TreeInfo.cpp",
@@ -628,6 +641,21 @@ cc_defaults {
// Allow implicit fallthroughs in HardwareBitmapUploader.cpp until they are fixed.
cflags: ["-Wno-implicit-fallthrough"],
},
+ host: {
+ header_libs: ["libnativebase_headers"],
+
+ local_include_dirs: ["platform/host"],
+
+ srcs: [
+ "platform/host/renderthread/CacheManager.cpp",
+ "platform/host/renderthread/RenderThread.cpp",
+ "platform/host/ProfileDataContainer.cpp",
+ "platform/host/Readback.cpp",
+ "platform/host/WebViewFunctorManager.cpp",
+ ],
+
+ cflags: ["-Wno-unused-private-field"],
+ },
},
}
@@ -663,6 +691,7 @@ cc_defaults {
header_libs: ["libandroid_headers_private"],
target: {
android: {
+ local_include_dirs: ["platform/android"],
shared_libs: [
"libgui",
"libui",
diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp
index 3d0ca0a10851..7be9541f6b99 100644
--- a/libs/hwui/ProfileData.cpp
+++ b/libs/hwui/ProfileData.cpp
@@ -110,6 +110,7 @@ void ProfileData::mergeWith(const ProfileData& other) {
}
void ProfileData::dump(int fd) const {
+#ifdef __ANDROID__
dprintf(fd, "\nStats since: %" PRIu64 "ns", mStatStartTime);
dprintf(fd, "\nTotal frames rendered: %u", mTotalFrameCount);
dprintf(fd, "\nJanky frames: %u (%.2f%%)", mJankFrameCount,
@@ -138,6 +139,7 @@ void ProfileData::dump(int fd) const {
dprintf(fd, " %ums=%u", entry.renderTimeMs, entry.frameCount);
});
dprintf(fd, "\n");
+#endif
}
uint32_t ProfileData::findPercentile(int percentile) const {
diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h
index 02bf0d8d5e95..1fcb6920db14 100644
--- a/libs/hwui/hwui/DrawTextFunctor.h
+++ b/libs/hwui/hwui/DrawTextFunctor.h
@@ -92,6 +92,7 @@ public:
// high contrast draw path
int color = paint.getColor();
bool darken;
+ // This equation should match the one in core/java/android/text/Layout.java
if (flags::high_contrast_text_luminance()) {
uirenderer::Lab lab = uirenderer::sRGBToLab(color);
darken = lab.L <= 50;
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index 1fc34d633370..9b63a46822ac 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -88,6 +88,10 @@ static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHand
get_canvas(canvasHandle)->setBitmap(bitmap);
}
+static jboolean isHighContrastText(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
+ return get_canvas(canvasHandle)->isHighContrastText() ? JNI_TRUE : JNI_FALSE;
+}
+
static jboolean isOpaque(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
}
@@ -792,6 +796,7 @@ static const JNINativeMethod gMethods[] = {
// ------------ @CriticalNative ----------------
{"nIsOpaque", "(J)Z", (void*)CanvasJNI::isOpaque},
+ {"nIsHighContrastText", "(J)Z", (void*)CanvasJNI::isHighContrastText},
{"nGetWidth", "(J)I", (void*)CanvasJNI::getWidth},
{"nGetHeight", "(J)I", (void*)CanvasJNI::getHeight},
{"nSave", "(JI)I", (void*)CanvasJNI::save},
diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/platform/android/thread/ThreadBase.h
index 0289d3fd2ef7..2f3581f8b355 100644
--- a/libs/hwui/thread/ThreadBase.h
+++ b/libs/hwui/platform/android/thread/ThreadBase.h
@@ -17,14 +17,14 @@
#ifndef HWUI_THREADBASE_H
#define HWUI_THREADBASE_H
-#include "WorkQueue.h"
-#include "utils/Macros.h"
-
#include <utils/Looper.h>
#include <utils/Thread.h>
#include <algorithm>
+#include "thread/WorkQueue.h"
+#include "utils/Macros.h"
+
namespace android::uirenderer {
class ThreadBase : public Thread {
diff --git a/libs/hwui/platform/host/ProfileDataContainer.cpp b/libs/hwui/platform/host/ProfileDataContainer.cpp
new file mode 100644
index 000000000000..9ed1b02a8082
--- /dev/null
+++ b/libs/hwui/platform/host/ProfileDataContainer.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ProfileDataContainer.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace uirenderer {
+
+void ProfileDataContainer::freeData() REQUIRES(mJankDataMutex) {
+ delete mData;
+ mIsMapped = false;
+ mData = nullptr;
+}
+
+void ProfileDataContainer::rotateStorage() {
+ std::lock_guard lock(mJankDataMutex);
+ mData->reset();
+}
+
+void ProfileDataContainer::switchStorageToAshmem(int ashmemfd) {
+ ALOGE("Ashmem is not supported for non-Android configurations");
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/Readback.cpp b/libs/hwui/platform/host/Readback.cpp
new file mode 100644
index 000000000000..b024ec02efc3
--- /dev/null
+++ b/libs/hwui/platform/host/Readback.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Readback.h"
+
+using namespace android::uirenderer::renderthread;
+
+namespace android {
+namespace uirenderer {
+
+void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<CopyRequest>& request) {
+}
+
+CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
+ return CopyResult::UnknownError;
+}
+
+CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
+ return CopyResult::UnknownError;
+}
+
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap) {
+ return CopyResult::UnknownError;
+}
+
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect,
+ SkBitmap* bitmap) {
+ return CopyResult::UnknownError;
+}
+
+bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
+ SkBitmap* bitmap) {
+ return false;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/WebViewFunctorManager.cpp b/libs/hwui/platform/host/WebViewFunctorManager.cpp
new file mode 100644
index 000000000000..1d16655bf73c
--- /dev/null
+++ b/libs/hwui/platform/host/WebViewFunctorManager.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "WebViewFunctorManager.h"
+
+namespace android::uirenderer {
+
+WebViewFunctor::WebViewFunctor(void* data, const WebViewFunctorCallbacks& callbacks,
+ RenderMode functorMode)
+ : mData(data) {}
+
+WebViewFunctor::~WebViewFunctor() {}
+
+void WebViewFunctor::sync(const WebViewSyncData& syncData) const {}
+
+void WebViewFunctor::onRemovedFromTree() {}
+
+bool WebViewFunctor::prepareRootSurfaceControl() {
+ return true;
+}
+
+void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {}
+
+void WebViewFunctor::initVk(const VkFunctorInitParams& params) {}
+
+void WebViewFunctor::drawVk(const VkFunctorDrawParams& params) {}
+
+void WebViewFunctor::postDrawVk() {}
+
+void WebViewFunctor::destroyContext() {}
+
+void WebViewFunctor::removeOverlays() {}
+
+ASurfaceControl* WebViewFunctor::getSurfaceControl() {
+ return mSurfaceControl;
+}
+
+void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) {}
+
+void WebViewFunctor::reparentSurfaceControl(ASurfaceControl* parent) {}
+
+WebViewFunctorManager& WebViewFunctorManager::instance() {
+ static WebViewFunctorManager sInstance;
+ return sInstance;
+}
+
+int WebViewFunctorManager::createFunctor(void* data, const WebViewFunctorCallbacks& callbacks,
+ RenderMode functorMode) {
+ return 0;
+}
+
+void WebViewFunctorManager::releaseFunctor(int functor) {}
+
+void WebViewFunctorManager::onContextDestroyed() {}
+
+void WebViewFunctorManager::destroyFunctor(int functor) {}
+
+sp<WebViewFunctor::Handle> WebViewFunctorManager::handleFor(int functor) {
+ return nullptr;
+}
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/platform/host/renderthread/CacheManager.cpp b/libs/hwui/platform/host/renderthread/CacheManager.cpp
new file mode 100644
index 000000000000..b03f400cd7d6
--- /dev/null
+++ b/libs/hwui/platform/host/renderthread/CacheManager.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "renderthread/CacheManager.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+CacheManager::CacheManager(RenderThread& thread)
+ : mRenderThread(thread), mMemoryPolicy(loadMemoryPolicy()) {}
+
+void CacheManager::setupCacheLimits() {}
+
+void CacheManager::destroy() {}
+
+void CacheManager::trimMemory(TrimLevel mode) {}
+
+void CacheManager::trimCaches(CacheTrimLevel mode) {}
+
+void CacheManager::trimStaleResources() {}
+
+void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {}
+
+void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {}
+
+void CacheManager::onFrameCompleted() {}
+
+void CacheManager::onThreadIdle() {}
+
+void CacheManager::scheduleDestroyContext() {}
+
+void CacheManager::cancelDestroyContext() {}
+
+bool CacheManager::areAllContextsStopped() {
+ return false;
+}
+
+void CacheManager::checkUiHidden() {}
+
+void CacheManager::registerCanvasContext(CanvasContext* context) {}
+
+void CacheManager::unregisterCanvasContext(CanvasContext* context) {}
+
+void CacheManager::onContextStopped(CanvasContext* context) {}
+
+void CacheManager::notifyNextFrameSize(int width, int height) {}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/renderthread/RenderThread.cpp b/libs/hwui/platform/host/renderthread/RenderThread.cpp
new file mode 100644
index 000000000000..6f08b5979772
--- /dev/null
+++ b/libs/hwui/platform/host/renderthread/RenderThread.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "renderthread/RenderThread.h"
+
+#include "Readback.h"
+#include "renderthread/VulkanManager.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+static bool gHasRenderThreadInstance = false;
+static JVMAttachHook gOnStartHook = nullptr;
+
+ASurfaceControlFunctions::ASurfaceControlFunctions() {}
+
+bool RenderThread::hasInstance() {
+ return gHasRenderThreadInstance;
+}
+
+void RenderThread::setOnStartHook(JVMAttachHook onStartHook) {
+ LOG_ALWAYS_FATAL_IF(hasInstance(), "can't set an onStartHook after we've started...");
+ gOnStartHook = onStartHook;
+}
+
+JVMAttachHook RenderThread::getOnStartHook() {
+ return gOnStartHook;
+}
+
+RenderThread& RenderThread::getInstance() {
+ [[clang::no_destroy]] static sp<RenderThread> sInstance = []() {
+ sp<RenderThread> thread = sp<RenderThread>::make();
+ thread->start("RenderThread");
+ return thread;
+ }();
+ gHasRenderThreadInstance = true;
+ return *sInstance;
+}
+
+RenderThread::RenderThread()
+ : ThreadBase()
+ , mVsyncSource(nullptr)
+ , mVsyncRequested(false)
+ , mFrameCallbackTaskPending(false)
+ , mRenderState(nullptr)
+ , mEglManager(nullptr)
+ , mFunctorManager(WebViewFunctorManager::instance())
+ , mGlobalProfileData(mJankDataMutex) {
+ Properties::load();
+}
+
+RenderThread::~RenderThread() {}
+
+void RenderThread::initThreadLocals() {
+ mCacheManager = new CacheManager(*this);
+}
+
+void RenderThread::requireGlContext() {}
+
+void RenderThread::requireVkContext() {}
+
+void RenderThread::initGrContextOptions(GrContextOptions& options) {}
+
+void RenderThread::destroyRenderingContext() {}
+
+VulkanManager& RenderThread::vulkanManager() {
+ return *mVkManager;
+}
+
+void RenderThread::dumpGraphicsMemory(int fd, bool includeProfileData) {}
+
+void RenderThread::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {}
+
+Readback& RenderThread::readback() {
+ if (!mReadback) {
+ mReadback = new Readback(*this);
+ }
+
+ return *mReadback;
+}
+
+void RenderThread::setGrContext(sk_sp<GrDirectContext> context) {}
+
+sk_sp<GrDirectContext> RenderThread::requireGrContext() {
+ return mGrContext;
+}
+
+bool RenderThread::threadLoop() {
+ if (gOnStartHook) {
+ gOnStartHook("RenderThread");
+ }
+ initThreadLocals();
+
+ while (true) {
+ waitForWork();
+ processQueue();
+ mCacheManager->onThreadIdle();
+ }
+
+ return false;
+}
+
+void RenderThread::postFrameCallback(IFrameCallback* callback) {}
+
+bool RenderThread::removeFrameCallback(IFrameCallback* callback) {
+ return false;
+}
+
+void RenderThread::pushBackFrameCallback(IFrameCallback* callback) {}
+
+sk_sp<Bitmap> RenderThread::allocateHardwareBitmap(SkBitmap& skBitmap) {
+ return nullptr;
+}
+
+bool RenderThread::isCurrent() {
+ return true;
+}
+
+void RenderThread::preload() {}
+
+void RenderThread::trimMemory(TrimLevel level) {}
+
+void RenderThread::trimCaches(CacheTrimLevel level) {}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/thread/ThreadBase.h b/libs/hwui/platform/host/thread/ThreadBase.h
new file mode 100644
index 000000000000..d709430cc9b6
--- /dev/null
+++ b/libs/hwui/platform/host/thread/ThreadBase.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HWUI_THREADBASE_H
+#define HWUI_THREADBASE_H
+
+#include <utils/Thread.h>
+
+#include <algorithm>
+
+#include "thread/WorkQueue.h"
+#include "utils/Macros.h"
+
+namespace android::uirenderer {
+
+class ThreadBase : public Thread {
+ PREVENT_COPY_AND_ASSIGN(ThreadBase);
+
+public:
+ ThreadBase() : Thread(false), mQueue([this]() { mCondition.notify_all(); }, mLock) {}
+
+ WorkQueue& queue() { return mQueue; }
+
+ void requestExit() { Thread::requestExit(); }
+
+ void start(const char* name = "ThreadBase") { Thread::run(name); }
+
+ void join() { Thread::join(); }
+
+ bool isRunning() const { return Thread::isRunning(); }
+
+protected:
+ void waitForWork() {
+ std::unique_lock lock{mLock};
+ nsecs_t nextWakeup = mQueue.nextWakeup(lock);
+ std::chrono::nanoseconds duration = std::chrono::nanoseconds::max();
+ if (nextWakeup < std::numeric_limits<nsecs_t>::max()) {
+ int timeout = nextWakeup - WorkQueue::clock::now();
+ if (timeout < 0) timeout = 0;
+ duration = std::chrono::nanoseconds(timeout);
+ }
+ mCondition.wait_for(lock, duration);
+ }
+
+ void processQueue() { mQueue.process(); }
+
+ virtual bool threadLoop() override {
+ while (!exitPending()) {
+ waitForWork();
+ processQueue();
+ }
+ return false;
+ }
+
+private:
+ WorkQueue mQueue;
+ std::mutex mLock;
+ std::condition_variable mCondition;
+};
+
+} // namespace android::uirenderer
+
+#endif // HWUI_THREADBASE_H
diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h
index 22ae59e5137b..493c943079ab 100644
--- a/libs/hwui/private/hwui/WebViewFunctor.h
+++ b/libs/hwui/private/hwui/WebViewFunctor.h
@@ -17,15 +17,7 @@
#ifndef FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
#define FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
-#ifdef __ANDROID__ // Layoutlib does not support surface control
#include <android/surface_control.h>
-#else
-// To avoid ifdefs around overlay implementation all over the place we typedef these to void *. They
-// won't be used.
-typedef void* ASurfaceControl;
-typedef void* ASurfaceTransaction;
-#endif
-
#include <cutils/compiler.h>
#include <private/hwui/DrawGlInfo.h>
#include <private/hwui/DrawVkInfo.h>
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 623ee4e6c27e..a024aeb285f9 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -17,11 +17,12 @@
#include "RenderThread.h"
#include <GrContextOptions.h>
-#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
#include <android-base/properties.h>
#include <dlfcn.h>
#include <gl/GrGLInterface.h>
#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
+#include <private/android/choreographer.h>
#include <sys/resource.h>
#include <ui/FatVector.h>
#include <utils/Condition.h>
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 79e57de9d66f..045d26f1d329 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -20,10 +20,7 @@
#include <GrDirectContext.h>
#include <SkBitmap.h>
#include <cutils/compiler.h>
-#include <private/android/choreographer.h>
#include <surface_control_private.h>
-#include <thread/ThreadBase.h>
-#include <utils/Looper.h>
#include <utils/Thread.h>
#include <memory>
diff --git a/media/java/android/media/FadeManagerConfiguration.java b/media/java/android/media/FadeManagerConfiguration.java
index 4f1a8ee5e728..e6ec2c330340 100644
--- a/media/java/android/media/FadeManagerConfiguration.java
+++ b/media/java/android/media/FadeManagerConfiguration.java
@@ -21,6 +21,7 @@ import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURA
import android.annotation.DurationMillisLong;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -274,7 +275,7 @@ public final class FadeManagerConfiguration implements Parcelable {
* @throws IllegalArgumentException if the usage is invalid
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
- @DurationMillisLong
+ @IntRange(from = 0) @DurationMillisLong
public long getFadeOutDurationForUsage(@AudioAttributes.AttributeUsage int usage) {
ensureFadingIsEnabled();
validateUsage(usage);
@@ -290,7 +291,7 @@ public final class FadeManagerConfiguration implements Parcelable {
* @throws IllegalArgumentException if the usage is invalid
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
- @DurationMillisLong
+ @IntRange(from = 0) @DurationMillisLong
public long getFadeInDurationForUsage(@AudioAttributes.AttributeUsage int usage) {
ensureFadingIsEnabled();
validateUsage(usage);
@@ -345,7 +346,7 @@ public final class FadeManagerConfiguration implements Parcelable {
* @throws NullPointerException if the audio attributes is {@code null}
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
- @DurationMillisLong
+ @IntRange(from = 0) @DurationMillisLong
public long getFadeOutDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes) {
ensureFadingIsEnabled();
return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper(
@@ -361,7 +362,7 @@ public final class FadeManagerConfiguration implements Parcelable {
* @throws NullPointerException if the audio attributes is {@code null}
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
- @DurationMillisLong
+ @IntRange(from = 0) @DurationMillisLong
public long getFadeInDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes) {
ensureFadingIsEnabled();
return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper(
@@ -428,7 +429,7 @@ public final class FadeManagerConfiguration implements Parcelable {
*
* @return delay in milliseconds
*/
- @DurationMillisLong
+ @IntRange(from = 0) @DurationMillisLong
public long getFadeInDelayForOffenders() {
return mFadeInDelayForOffendersMillis;
}
@@ -517,14 +518,14 @@ public final class FadeManagerConfiguration implements Parcelable {
/**
* Returns the default fade out duration (in milliseconds)
*/
- public static @DurationMillisLong long getDefaultFadeOutDurationMillis() {
+ public static @IntRange(from = 1) @DurationMillisLong long getDefaultFadeOutDurationMillis() {
return DEFAULT_FADE_OUT_DURATION_MS;
}
/**
* Returns the default fade in duration (in milliseconds)
*/
- public static @DurationMillisLong long getDefaultFadeInDurationMillis() {
+ public static @IntRange(from = 1) @DurationMillisLong long getDefaultFadeInDurationMillis() {
return DEFAULT_FADE_IN_DURATION_MS;
}
@@ -820,8 +821,8 @@ public final class FadeManagerConfiguration implements Parcelable {
* @param fadeOutDurationMillis duration in milliseconds used for fading out
* @param fadeInDurationMills duration in milliseconds used for fading in
*/
- public Builder(@DurationMillisLong long fadeOutDurationMillis,
- @DurationMillisLong long fadeInDurationMills) {
+ public Builder(@IntRange(from = 1) @DurationMillisLong long fadeOutDurationMillis,
+ @IntRange(from = 1) @DurationMillisLong long fadeInDurationMills) {
mFadeOutDurationMillis = fadeOutDurationMillis;
mFadeInDurationMillis = fadeInDurationMills;
}
@@ -939,7 +940,7 @@ public final class FadeManagerConfiguration implements Parcelable {
*/
@NonNull
public Builder setFadeOutDurationForUsage(@AudioAttributes.AttributeUsage int usage,
- @DurationMillisLong long fadeOutDurationMillis) {
+ @IntRange(from = 0) @DurationMillisLong long fadeOutDurationMillis) {
validateUsage(usage);
VolumeShaper.Configuration fadeOutVShaperConfig =
createVolShaperConfigForDuration(fadeOutDurationMillis, /* isFadeIn= */ false);
@@ -970,7 +971,7 @@ public final class FadeManagerConfiguration implements Parcelable {
*/
@NonNull
public Builder setFadeInDurationForUsage(@AudioAttributes.AttributeUsage int usage,
- @DurationMillisLong long fadeInDurationMillis) {
+ @IntRange(from = 0) @DurationMillisLong long fadeInDurationMillis) {
validateUsage(usage);
VolumeShaper.Configuration fadeInVShaperConfig =
createVolShaperConfigForDuration(fadeInDurationMillis, /* isFadeIn= */ true);
@@ -1055,7 +1056,7 @@ public final class FadeManagerConfiguration implements Parcelable {
@NonNull
public Builder setFadeOutDurationForAudioAttributes(
@NonNull AudioAttributes audioAttributes,
- @DurationMillisLong long fadeOutDurationMillis) {
+ @IntRange(from = 0) @DurationMillisLong long fadeOutDurationMillis) {
Objects.requireNonNull(audioAttributes, "Audio attribute cannot be null");
VolumeShaper.Configuration fadeOutVShaperConfig =
createVolShaperConfigForDuration(fadeOutDurationMillis, /* isFadeIn= */ false);
@@ -1087,7 +1088,7 @@ public final class FadeManagerConfiguration implements Parcelable {
*/
@NonNull
public Builder setFadeInDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes,
- @DurationMillisLong long fadeInDurationMillis) {
+ @IntRange(from = 0) @DurationMillisLong long fadeInDurationMillis) {
Objects.requireNonNull(audioAttributes, "Audio attribute cannot be null");
VolumeShaper.Configuration fadeInVShaperConfig =
createVolShaperConfigForDuration(fadeInDurationMillis, /* isFadeIn= */ true);
@@ -1336,7 +1337,8 @@ public final class FadeManagerConfiguration implements Parcelable {
* @see #getFadeInDelayForOffenders()
*/
@NonNull
- public Builder setFadeInDelayForOffenders(@DurationMillisLong long delayMillis) {
+ public Builder setFadeInDelayForOffenders(
+ @IntRange(from = 0) @DurationMillisLong long delayMillis) {
Preconditions.checkArgument(delayMillis >= 0, "Delay cannot be negative");
mFadeInDelayForOffendersMillis = delayMillis;
return this;
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index b662901176e6..022278298875 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -697,6 +697,19 @@ public final class MediaBrowser {
});
}
+ private void onDisconnectRequested(ServiceCallbacks callback) {
+ mHandler.post(
+ () -> {
+ Log.i(TAG, "onDisconnectRequest for " + mServiceComponent);
+
+ if (!isCurrent(callback, "onDisconnectRequest")) {
+ return;
+ }
+ forceCloseConnection();
+ mCallback.onDisconnected();
+ });
+ }
+
/**
* Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not.
*/
@@ -880,6 +893,19 @@ public final class MediaBrowser {
*/
public void onConnectionFailed() {
}
+
+ /**
+ * Invoked after disconnecting by request of the {@link MediaBrowserService}.
+ *
+ * <p>The default implementation of this method calls {@link #onConnectionFailed()}.
+ *
+ * @hide
+ */
+ // TODO: b/185136506 - Consider publishing this API in the next window for API changes, if
+ // the need arises.
+ public void onDisconnected() {
+ onConnectionFailed();
+ }
}
/**
@@ -1112,6 +1138,14 @@ public final class MediaBrowser {
mediaBrowser.onLoadChildren(this, parentId, list, options);
}
}
+
+ @Override
+ public void onDisconnect() {
+ MediaBrowser mediaBrowser = mMediaBrowser.get();
+ if (mediaBrowser != null) {
+ mediaBrowser.onDisconnectRequested(this);
+ }
+ }
}
private static class Subscription {
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 8dba04066ad9..6cf9c6fa7616 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -104,3 +104,10 @@ flag {
description: "Enable new MediaRouter2 API to enable watch companion apps to scan while the phone screen is off."
bug: "281072508"
}
+
+flag {
+ name: "enable_null_session_in_media_browser_service"
+ namespace: "media_solutions"
+ description: "Enables apps owning a MediaBrowserService to disconnect all connected browsers."
+ bug: "263520343"
+}
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index ae0c2ab0e811..45b4370d6cc9 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -478,7 +478,7 @@ MIDI 1.0 virtual devices, android.media.midi.MidiUmpDeviceService is used</p>
&lt;intent-filter>
&lt;action android:name="android.media.midi.MidiUmpDeviceService" />
&lt;/intent-filter>
- &lt;meta-data android:name="android.media.midi.MidiUmpDeviceService"
+ &lt;property android:name="android.media.midi.MidiUmpDeviceService"
android:resource="@xml/<strong>echo_device_info</strong>" />
&lt;/service>
</pre>
diff --git a/media/java/android/media/tv/SignalingDataResponse.java b/media/java/android/media/tv/SignalingDataResponse.java
index be172ec62773..51fa6a23bdf6 100644
--- a/media/java/android/media/tv/SignalingDataResponse.java
+++ b/media/java/android/media/tv/SignalingDataResponse.java
@@ -73,6 +73,10 @@ public final class SignalingDataResponse extends BroadcastInfoResponse implement
/**
* Gets a list of types of metadata that are contained in this response.
*
+ * <p> This list correlates to all the available types that can be found within
+ * {@link #getSignalingDataInfoList()}. This list is determined by the types specified in
+ * {@link SignalingDataRequest#getSignalingDataTypes()}.
+ *
* <p> A list of types available are defined in {@link SignalingDataRequest}.
* For more information about these types, see A/344:2023-5 9.2.10 - Query Signaling Data API.
*
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index f332f8102013..84d08db1b9c8 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -964,7 +964,11 @@ public abstract class TvInteractiveAppService extends Service {
/**
* Called when the TV App sends the selected track info as a response to
- * {@link #requestSelectedTrackInfo()}
+ * {@link #requestSelectedTrackInfo()}.
+ *
+ * <p> When a selected track changes as a result of a new selection,
+ * {@link #onTrackSelected(int, String)} should be used instead to communicate the specific
+ * track selection.
*
* @param tracks A list of {@link TvTrackInfo} that are currently selected
*/
@@ -1383,6 +1387,8 @@ public abstract class TvInteractiveAppService extends Service {
* <p> Normally, track info cannot be synchronized until the channel has
* been changed. This is used when the session of the {@link TvInteractiveAppService}
* is newly created and the normal synchronization has not happened yet.
+ *
+ * <p> The track info will be returned in {@link #onSelectedTrackInfo(List)}
*/
@FlaggedApi(Flags.FLAG_TIAF_V_APIS)
@CallSuper
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 29a3b98073fe..635572d12cc5 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -585,7 +585,8 @@ public class TvInteractiveAppView extends ViewGroup {
}
/**
- * Sends the currently selected track info to the TV Interactive App.
+ * Sends the currently selected track info to the TV Interactive App in response to a
+ * {@link TvInteractiveAppCallback#onRequestSelectedTrackInfo(String)} request.
*
* @param tracks list of {@link TvTrackInfo} of the currently selected track(s)
*/
diff --git a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
index a8772076af97..fbb7cfd5ded1 100644
--- a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
+++ b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
@@ -24,4 +24,11 @@ oneway interface IMediaBrowserServiceCallbacks {
@UnsupportedAppUsage
void onConnectFailed();
void onLoadChildren(String mediaId, in ParceledListSlice list, in Bundle options);
+ /**
+ * Invoked when the browser service cuts off the connection with the browser.
+ *
+ * <p>The browser must also clean up any state associated with this connection, as if the
+ * service had been destroyed.
+ */
+ void onDisconnect();
}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index d17b341782ce..39ef528fb69b 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -38,10 +38,13 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
+import com.android.media.flags.Flags;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -51,6 +54,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Base class for media browser services.
@@ -96,6 +100,7 @@ public abstract class MediaBrowserService extends Service {
private static final int RESULT_ERROR = -1;
private static final int RESULT_OK = 0;
+ private final ServiceBinder mBinder;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -105,7 +110,7 @@ public abstract class MediaBrowserService extends Service {
private final Handler mHandler = new Handler();
- private final ServiceState mServiceState = new ServiceState();
+ private final AtomicReference<ServiceState> mServiceState;
// Holds the connection record associated with the currently executing callback operation, if
// any. See getCurrentBrowserInfo for an example. Must only be accessed on mHandler.
@@ -216,16 +221,21 @@ public abstract class MediaBrowserService extends Service {
}
private static class ServiceBinder extends IMediaBrowserService.Stub {
- private WeakReference<ServiceState> mServiceState;
+ private final AtomicReference<WeakReference<ServiceState>> mServiceState;
private ServiceBinder(ServiceState serviceState) {
- mServiceState = new WeakReference(serviceState);
+ mServiceState = new AtomicReference<>();
+ setServiceState(serviceState);
+ }
+
+ public void setServiceState(ServiceState serviceState) {
+ mServiceState.set(new WeakReference<>(serviceState));
}
@Override
public void connect(final String pkg, final Bundle rootHints,
final IMediaBrowserServiceCallbacks callbacks) {
- ServiceState serviceState = mServiceState.get();
+ ServiceState serviceState = mServiceState.get().get();
if (serviceState == null) {
return;
}
@@ -243,7 +253,7 @@ public abstract class MediaBrowserService extends Service {
@Override
public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
- ServiceState serviceState = mServiceState.get();
+ ServiceState serviceState = mServiceState.get().get();
if (serviceState == null) {
return;
}
@@ -260,7 +270,7 @@ public abstract class MediaBrowserService extends Service {
@Override
public void addSubscription(final String id, final IBinder token, final Bundle options,
final IMediaBrowserServiceCallbacks callbacks) {
- ServiceState serviceState = mServiceState.get();
+ ServiceState serviceState = mServiceState.get().get();
if (serviceState == null) {
return;
}
@@ -278,7 +288,7 @@ public abstract class MediaBrowserService extends Service {
@Override
public void removeSubscription(final String id, final IBinder token,
final IMediaBrowserServiceCallbacks callbacks) {
- ServiceState serviceState = mServiceState.get();
+ ServiceState serviceState = mServiceState.get().get();
if (serviceState == null) {
return;
}
@@ -294,7 +304,7 @@ public abstract class MediaBrowserService extends Service {
@Override
public void getMediaItem(final String mediaId, final ResultReceiver receiver,
final IMediaBrowserServiceCallbacks callbacks) {
- ServiceState serviceState = mServiceState.get();
+ ServiceState serviceState = mServiceState.get().get();
if (serviceState == null) {
return;
}
@@ -304,17 +314,23 @@ public abstract class MediaBrowserService extends Service {
}
}
+ /** Default constructor. */
+ public MediaBrowserService() {
+ mServiceState = new AtomicReference<>(new ServiceState());
+ mBinder = new ServiceBinder(mServiceState.get());
+ }
+
@Override
public void onCreate() {
super.onCreate();
- mServiceState.mBinder = new ServiceBinder(mServiceState);
}
@Override
public IBinder onBind(Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mServiceState.mBinder;
+ return mBinder;
}
+
return null;
}
@@ -428,21 +444,33 @@ public abstract class MediaBrowserService extends Service {
/**
* Call to set the media session.
- * <p>
- * This should be called as soon as possible during the service's startup.
- * It may only be called once.
+ *
+ * <p>This should be called as soon as possible during the service's startup. It may only be
+ * called once.
*
* @param token The token for the service's {@link MediaSession}.
*/
+ // TODO: b/185136506 - Update the javadoc to reflect API changes when
+ // enableNullSessionInMediaBrowserService makes it to nextfood.
public void setSessionToken(final MediaSession.Token token) {
+ ServiceState serviceState = mServiceState.get();
if (token == null) {
- throw new IllegalArgumentException("Session token may not be null.");
- }
- if (mServiceState.mSession != null) {
+ if (!Flags.enableNullSessionInMediaBrowserService()) {
+ throw new IllegalArgumentException("Session token may not be null.");
+ } else if (serviceState.mSession != null) {
+ ServiceState newServiceState = new ServiceState();
+ mBinder.setServiceState(newServiceState);
+ mServiceState.set(newServiceState);
+ serviceState.release();
+ } else {
+ // Nothing to do. The session is already null.
+ }
+ } else if (serviceState.mSession != null) {
throw new IllegalStateException("The session token has already been set.");
+ } else {
+ serviceState.mSession = token;
+ mHandler.post(() -> serviceState.notifySessionTokenInitializedOnHandler(token));
}
- mServiceState.mSession = token;
- mHandler.post(() -> mServiceState.notifySessionTokenInitializedOnHandler(token));
}
/**
@@ -450,7 +478,7 @@ public abstract class MediaBrowserService extends Service {
* or if it has been destroyed.
*/
public @Nullable MediaSession.Token getSessionToken() {
- return mServiceState.mSession;
+ return mServiceState.get().mSession;
}
/**
@@ -521,7 +549,7 @@ public abstract class MediaBrowserService extends Service {
if (parentId == null) {
throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
}
- mHandler.post(() -> mServiceState.notifyChildrenChangeOnHandler(parentId, options));
+ mHandler.post(() -> mServiceState.get().notifyChildrenChangeOnHandler(parentId, options));
}
/**
@@ -623,15 +651,38 @@ public abstract class MediaBrowserService extends Service {
// Fields accessed from any caller thread.
@Nullable private MediaSession.Token mSession;
- @Nullable private ServiceBinder mBinder;
// Fields accessed from mHandler only.
@NonNull private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
+ public ServiceBinder getBinder() {
+ return mBinder;
+ }
+
public void postOnHandler(Runnable runnable) {
mHandler.post(runnable);
}
+ public void release() {
+ mHandler.postAtFrontOfQueue(this::clearConnectionsOnHandler);
+ }
+
+ private void clearConnectionsOnHandler() {
+ Iterator<ConnectionRecord> iterator = mConnections.values().iterator();
+ while (iterator.hasNext()) {
+ ConnectionRecord record = iterator.next();
+ iterator.remove();
+ try {
+ record.callbacks.onDisconnect();
+ } catch (RemoteException exception) {
+ Log.w(
+ TAG,
+ TextUtils.formatSimple("onDisconnectRequest for %s failed", record.pkg),
+ exception);
+ }
+ }
+ }
+
public void removeConnectionRecordOnHandler(IMediaBrowserServiceCallbacks callbacks) {
IBinder b = callbacks.asBinder();
// Clear out the old subscriptions. We are getting new ones.
@@ -796,8 +847,7 @@ public abstract class MediaBrowserService extends Service {
@Override
void onResultSent(
List<MediaBrowser.MediaItem> list, @ResultFlags int flag) {
- if (mServiceState.mConnections.get(connection.callbacks.asBinder())
- != connection) {
+ if (mConnections.get(connection.callbacks.asBinder()) != connection) {
if (DBG) {
Log.d(
TAG,
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 6efb0280ac02..53699bc706ea 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -87,7 +87,7 @@ int64_t AKeyEvent_getDownTime(const AInputEvent* key_event) {
const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent) {
std::unique_ptr<KeyEvent> event = std::make_unique<KeyEvent>();
- *event = android::android_view_KeyEvent_toNative(env, keyEvent);
+ *event = android::android_view_KeyEvent_obtainAsCopy(env, keyEvent);
return event.release();
}
@@ -321,11 +321,13 @@ jobject AInputEvent_toJava(JNIEnv* env, const AInputEvent* aInputEvent) {
case AINPUT_EVENT_TYPE_MOTION:
return android::android_view_MotionEvent_obtainAsCopy(env,
static_cast<const MotionEvent&>(
- *aInputEvent));
+ *aInputEvent))
+ .release();
case AINPUT_EVENT_TYPE_KEY:
- return android::android_view_KeyEvent_fromNative(env,
- static_cast<const KeyEvent&>(
- *aInputEvent));
+ return android::android_view_KeyEvent_obtainAsCopy(env,
+ static_cast<const KeyEvent&>(
+ *aInputEvent))
+ .release();
default:
LOG_ALWAYS_FATAL("Unexpected event type %d in AInputEvent_toJava.", eventType);
}
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index e244520d1687..7b53ca6ea7e9 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -82,7 +82,7 @@ package android.nfc {
method @FlaggedApi("android.nfc.enable_nfc_charging") public boolean isWlcEnabled();
method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void resetDiscoveryTechnology(@NonNull android.app.Activity);
method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void setDiscoveryTechnology(@NonNull android.app.Activity, int, int);
- method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setTransactionAllowed(boolean);
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setObserveModeEnabled(boolean);
field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED";
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index e5752d158c12..0ebc3f5178e0 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -1250,16 +1250,16 @@ public final class NfcAdapter {
* and simply observe and notify the APDU service of polling loop frames. See
* {@link #isObserveModeSupported()} for a description of observe mode.
*
- * @param allowed true disables observe mode to allow the transaction to proceed while false
+ * @param enabled false disables observe mode to allow the transaction to proceed while true
* enables observe mode and does not allow transactions to proceed.
*
* @return boolean indicating success or failure.
*/
@FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
- public boolean setTransactionAllowed(boolean allowed) {
+ public boolean setObserveModeEnabled(boolean enabled) {
try {
- return sService.setObserveMode(!allowed);
+ return sService.setObserveMode(enabled);
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
return false;
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index 32dba5f746a9..c81b95b7c81b 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -176,11 +176,25 @@ public final class ApduServiceInfo implements Parcelable {
List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid,
String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled) {
+ this(info, onHost, description, staticAidGroups, dynamicAidGroups,
+ requiresUnlock, requiresScreenOn, bannerResource, uid,
+ settingsActivityName, offHost, staticOffHost, isEnabled,
+ new HashMap<String, Boolean>());
+ }
+
+ /**
+ * @hide
+ */
+ public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
+ List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
+ boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid,
+ String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled,
+ HashMap<String, Boolean> autoTransact) {
this.mService = info;
this.mDescription = description;
this.mStaticAidGroups = new HashMap<String, AidGroup>();
this.mDynamicAidGroups = new HashMap<String, AidGroup>();
- this.mAutoTransact = new HashMap<String, Boolean>();
+ this.mAutoTransact = autoTransact;
this.mOffHostName = offHost;
this.mStaticOffHostName = staticOffHost;
this.mOnHost = onHost;
@@ -196,7 +210,6 @@ public final class ApduServiceInfo implements Parcelable {
this.mUid = uid;
this.mSettingsActivityName = settingsActivityName;
this.mCategoryOtherServiceEnabled = isEnabled;
-
}
/**
@@ -682,7 +695,7 @@ public final class ApduServiceInfo implements Parcelable {
/**
* Add a Polling Loop Filter. Custom NFC polling frames that match this filter will cause the
* device to exit observe mode, just as if
- * {@link android.nfc.NfcAdapter#setTransactionAllowed(boolean)} had been called with true,
+ * {@link android.nfc.NfcAdapter#setObserveModeEnabled(boolean)} had been called with true,
* allowing transactions to proceed. The matching frame will also be delivered to
* {@link HostApduService#processPollingFrames(List)}. Adding a key with this or
* {@link ApduServiceInfo#addPollingLoopFilter(String)} multiple times will
@@ -857,6 +870,8 @@ public final class ApduServiceInfo implements Parcelable {
dest.writeString(mSettingsActivityName);
dest.writeInt(mCategoryOtherServiceEnabled ? 1 : 0);
+ dest.writeInt(mAutoTransact.size());
+ dest.writeMap(mAutoTransact);
};
@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@@ -885,10 +900,15 @@ public final class ApduServiceInfo implements Parcelable {
int uid = source.readInt();
String settingsActivityName = source.readString();
boolean isEnabled = source.readInt() != 0;
+ int autoTransactSize = source.readInt();
+ HashMap<String, Boolean> autoTransact =
+ new HashMap<String, Boolean>(autoTransactSize);
+ source.readMap(autoTransact, getClass().getClassLoader(),
+ String.class, Boolean.class);
return new ApduServiceInfo(info, onHost, description, staticAidGroups,
dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid,
settingsActivityName, offHostName, staticOffHostName,
- isEnabled);
+ isEnabled, autoTransact);
}
@Override
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
index eccf6047b90c..72f67d93383b 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
@@ -494,6 +494,10 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
}
private void onUserCanceled(@NonNull Context context, @NonNull Intent intent) {
+ if (!isIntentValid(intent)) {
+ loge("Ignoring onUserCanceled called with invalid intent.");
+ return;
+ }
int capability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
logd("onUserCanceled: " + TelephonyManager.convertPremiumCapabilityToString(capability));
diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java
index 3c8ef6ed0550..8989aab61f1b 100644
--- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java
+++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java
@@ -262,10 +262,10 @@ public class SlicePurchaseBroadcastReceiverTest {
@Test
public void testNotificationCanceled() {
+ displayPerformanceBoostNotification();
+
// send ACTION_NOTIFICATION_CANCELED
doReturn("com.android.phone.slice.action.NOTIFICATION_CANCELED").when(mIntent).getAction();
- doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra(
- eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt());
mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent);
// verify notification was canceled
@@ -276,7 +276,7 @@ public class SlicePurchaseBroadcastReceiverTest {
}
@Test
- public void testNotificationTimeout() throws Exception {
+ public void testNotificationTimeout() {
displayPerformanceBoostNotification();
// send ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT
@@ -353,7 +353,7 @@ public class SlicePurchaseBroadcastReceiverTest {
verify(mNotificationManager, never()).notifyAsUser(
eq(SlicePurchaseBroadcastReceiver.PERFORMANCE_BOOST_NOTIFICATION_TAG),
eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY),
- any(),
+ any(Notification.class),
eq(UserHandle.ALL));
verify(mNotificationShownIntent, never()).send();
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index d910f2fc9904..bc3ad0615ada 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -72,6 +72,6 @@
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Strøm apper og andre systemfunksjoner fra telefonen"</string>
<string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Se en liste over tilgjengelige enheter, og kontroller hvilken enhet som strømmer eller caster lyd eller video fra andre apper"</string>
- <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
- <string name="device_type" product="tablet" msgid="5038791954983067774">"nettbrett"</string>
+ <string name="device_type" product="default" msgid="8268703872070046263">"telefonen"</string>
+ <string name="device_type" product="tablet" msgid="5038791954983067774">"nettbrettet"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index 88f1204641ff..0af108052137 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -59,8 +59,10 @@
<style name="VendorHelperBackButton"
parent="@android:style/Widget.Material.Button.Borderless.Colored">
- <item name="android:layout_width">70dp</item>
+ <item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">48dp</item>
+ <item name="android:layout_marginStart">12dp</item>
+ <item name="android:layout_marginEnd">12dp</item>
<item name="android:textAllCaps">false</item>
<item name="android:textSize">14sp</item>
<item name="android:textColor">@android:color/system_neutral1_900</item>
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt
index 6f70cfb14238..6902b6fcc69d 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt
@@ -31,10 +31,7 @@ interface CredentialManagerClient {
fun updateRequest(intent: Intent)
/** Sends an error encountered during the UI. */
- fun sendError(
- @BaseDialogResult.ResultCode resultCode: Int,
- errorMessage: String? = null,
- )
+ fun sendError(@BaseDialogResult.ResultCode resultCode: Int)
/**
* Sends a response to the system service. The response
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
index 7f59ba3c4a13..ab70394057f3 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
@@ -48,9 +48,13 @@ class CredentialManagerClientImpl @Inject constructor(
override fun updateRequest(intent: Intent) {
- val request = intent.parse(
- context = context,
- )
+ val request: Request
+ try {
+ request = intent.parse(context)
+ } catch (e: Exception) {
+ sendError(BaseDialogResult.RESULT_CODE_DATA_PARSING_FAILURE)
+ return
+ }
Log.d(TAG, "Request parsed: $request, client instance: $this")
if (request is Request.Cancel || request is Request.Close) {
if (request.token != null && request.token != _requests.value?.token) {
@@ -61,8 +65,9 @@ class CredentialManagerClientImpl @Inject constructor(
_requests.value = request
}
- override fun sendError(resultCode: Int, errorMessage: String?) {
- TODO("b/300422310 - [Wear] Implement UI for cancellation request with message")
+ override fun sendError(resultCode: Int) {
+ Log.w(TAG, "Error occurred, resultCode: $resultCode, current request: ${ requests.value }")
+ requests.value?.sendCancellationCode(resultCode)
}
override fun sendResult(result: UserSelectionDialogResult) {
@@ -108,7 +113,7 @@ class CredentialManagerClientImpl @Inject constructor(
return entryInfo.shouldTerminateUiUponSuccessfulProviderResult
}
- private fun Request.Get.sendCancellationCode(cancelCode: Int) {
+ private fun Request.sendCancellationCode(cancelCode: Int) {
sendCancellationCode(
cancelCode = cancelCode,
requestToken = token,
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
index 4a1c260ae332..fd99275ebc8e 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -25,6 +25,8 @@ import com.android.credentialmanager.model.get.ProviderInfo
*/
sealed class Request private constructor(
open val token: IBinder?,
+ open val resultReceiver: ResultReceiver? = null,
+ open val finalResponseReceiver: ResultReceiver? = null,
) {
/**
@@ -48,10 +50,10 @@ sealed class Request private constructor(
*/
data class Get(
override val token: IBinder?,
- val resultReceiver: ResultReceiver?,
- val finalResponseReceiver: ResultReceiver?,
+ override val resultReceiver: ResultReceiver?,
+ override val finalResponseReceiver: ResultReceiver?,
val providerInfos: List<ProviderInfo>,
- ) : Request(token)
+ ) : Request(token, resultReceiver, finalResponseReceiver)
/**
* Request to start the create credentials flow.
*/
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 6100623cbb3c..879d64c761ec 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -110,11 +110,7 @@ class CredentialManagerRepo(
ResultReceiver::class.java
)
- isReqForAllOptions = intent.getBooleanExtra(
- Constants.EXTRA_REQ_FOR_ALL_OPTIONS,
- /*defaultValue=*/ false
- ) || (requestInfo?.isShowAllOptionsRequested ?: false) // TODO(b/323552850) - Remove
- // usage on Constants.EXTRA_REQ_FOR_ALL_OPTIONS once it is deprecated.
+ isReqForAllOptions = requestInfo?.isShowAllOptionsRequested ?: false
val cancellationRequest = getCancelUiRequest(intent)
val cancelUiRequestState = cancellationRequest?.let {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 121f207122a0..eef75c7b707b 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -40,6 +40,7 @@ import android.service.autofill.Field
import android.service.autofill.FillCallback
import android.service.autofill.FillRequest
import android.service.autofill.FillResponse
+import android.service.autofill.Flags
import android.service.autofill.InlinePresentation
import android.service.autofill.Presentations
import android.service.autofill.SaveCallback
@@ -250,7 +251,7 @@ class CredentialAutofillService : AutofillService() {
maxInlineItemCount = maxInlineItemCount.coerceAtMost(inlineMaxSuggestedCount)
val lastDropdownDatasetIndex = Settings.Global.getInt(this.contentResolver,
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
- (maxDropdownDisplayLimit - 1)).coerceAtMost(totalEntryCount - 1)
+ (maxDropdownDisplayLimit - 1)).coerceAtMost(totalEntryCount)
var i = 0
var datasetAdded = false
@@ -479,18 +480,28 @@ class CredentialAutofillService : AutofillService() {
val autofillIdToCredentialEntries:
MutableMap<AutofillId, ArrayList<Entry>> = mutableMapOf()
credentialEntryList.forEach entryLoop@{ credentialEntry ->
- val autofillId: AutofillId? = credentialEntry
- .frameworkExtrasIntent
- ?.getParcelableExtra(
- CredentialProviderService.EXTRA_AUTOFILL_ID,
- AutofillId::class.java)
- if (autofillId == null) {
- Log.e(TAG, "AutofillId is missing from credential entry. Credential" +
- " Integration might be disabled.")
- return@entryLoop
- }
- autofillIdToCredentialEntries.getOrPut(autofillId) { ArrayList() }
- .add(credentialEntry)
+ val intent = credentialEntry.frameworkExtrasIntent
+ intent?.getParcelableExtra(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
+ android.service.credentials.GetCredentialRequest::class.java)
+ ?.credentialOptions
+ ?.forEach { credentialOption ->
+ credentialOption.candidateQueryData.getParcelableArrayList(
+ CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId::class.java)
+ ?.forEach { autofillId ->
+ intent.putExtra(
+ CredentialProviderService.EXTRA_AUTOFILL_ID,
+ autofillId)
+ val entry = Entry(
+ credentialEntry.key,
+ credentialEntry.subkey,
+ credentialEntry.slice,
+ intent)
+ autofillIdToCredentialEntries
+ .getOrPut(autofillId) { ArrayList() }
+ .add(entry)
+ }
+ }
}
return autofillIdToCredentialEntries
}
@@ -573,23 +584,31 @@ class CredentialAutofillService : AutofillService() {
cmRequests: MutableList<CredentialOption>,
responseClientState: Bundle
) {
+ val traversedViewNodes: MutableSet<AutofillId> = mutableSetOf()
+ val credentialOptionsFromHints: MutableMap<String, CredentialOption> = mutableMapOf()
val windowNodes: List<AssistStructure.WindowNode> =
structure.run {
(0 until windowNodeCount).map { getWindowNodeAt(it) }
}
windowNodes.forEach { windowNode: AssistStructure.WindowNode ->
- traverseNodeForRequest(windowNode.rootViewNode, cmRequests, responseClientState)
+ traverseNodeForRequest(
+ windowNode.rootViewNode, cmRequests, responseClientState, traversedViewNodes,
+ credentialOptionsFromHints)
}
}
private fun traverseNodeForRequest(
viewNode: AssistStructure.ViewNode,
cmRequests: MutableList<CredentialOption>,
- responseClientState: Bundle
+ responseClientState: Bundle,
+ traversedViewNodes: MutableSet<AutofillId>,
+ credentialOptionsFromHints: MutableMap<String, CredentialOption>
) {
viewNode.autofillId?.let {
- cmRequests.addAll(getCredentialOptionsFromViewNode(viewNode, it, responseClientState))
+ cmRequests.addAll(getCredentialOptionsFromViewNode(viewNode, it, responseClientState,
+ traversedViewNodes, credentialOptionsFromHints))
+ traversedViewNodes.add(it)
}
val children: List<AssistStructure.ViewNode> =
@@ -598,22 +617,37 @@ class CredentialAutofillService : AutofillService() {
}
children.forEach { childNode: AssistStructure.ViewNode ->
- traverseNodeForRequest(childNode, cmRequests, responseClientState)
+ traverseNodeForRequest(childNode, cmRequests, responseClientState, traversedViewNodes,
+ credentialOptionsFromHints)
}
}
private fun getCredentialOptionsFromViewNode(
viewNode: AssistStructure.ViewNode,
autofillId: AutofillId,
- responseClientState: Bundle
+ responseClientState: Bundle,
+ traversedViewNodes: MutableSet<AutofillId>,
+ credentialOptionsFromHints: MutableMap<String, CredentialOption>
): MutableList<CredentialOption> {
- if (viewNode.credentialManagerRequest != null &&
- viewNode.credentialManagerCallback != null) {
- val options = viewNode.credentialManagerRequest?.getCredentialOptions()
- if (options != null) {
- return options
+ val credentialOptions: MutableList<CredentialOption> = mutableListOf()
+ if (Flags.autofillCredmanDevIntegration() && viewNode.credentialManagerRequest != null) {
+ viewNode.credentialManagerRequest
+ ?.getCredentialOptions()
+ ?.forEach { credentialOption ->
+ credentialOption.candidateQueryData
+ .getParcelableArrayList(
+ CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId::class.java)
+ ?.let { associatedAutofillIds ->
+ // Check whether any of the associated autofill ids have already been
+ // traversed. If so, skip, to dedupe on duplicate credential options.
+ if ((traversedViewNodes intersect associatedAutofillIds.toSet())
+ .isEmpty()) {
+ credentialOptions.add(credentialOption)
+ }
+ }
}
}
+ // TODO(b/325502552): clean up cred option logic in autofill hint
val credentialHints: MutableList<String> = mutableListOf()
if (viewNode.autofillHints != null) {
@@ -627,10 +661,10 @@ class CredentialAutofillService : AutofillService() {
}
}
- val credentialOptions: MutableList<CredentialOption> = mutableListOf()
for (credentialHint in credentialHints) {
try {
- convertJsonToCredentialOption(credentialHint, autofillId)
+ convertJsonToCredentialOption(
+ credentialHint, autofillId, credentialOptionsFromHints)
.let { credentialOptions.addAll(it) }
} catch (e: JSONException) {
Log.i(TAG, "Exception while parsing response: " + e.message)
@@ -639,10 +673,11 @@ class CredentialAutofillService : AutofillService() {
return credentialOptions
}
- private fun convertJsonToCredentialOption(jsonString: String, autofillId: AutofillId):
- List<CredentialOption> {
- // TODO(b/302000646) Move this logic to jetpack so that is consistent
- // with building the json
+ private fun convertJsonToCredentialOption(
+ jsonString: String,
+ autofillId: AutofillId,
+ credentialOptionsFromHints: MutableMap<String, CredentialOption>
+ ): List<CredentialOption> {
val credentialOptions: MutableList<CredentialOption> = mutableListOf()
val json = JSONObject(jsonString)
@@ -650,16 +685,34 @@ class CredentialAutofillService : AutofillService() {
val options = jsonGet.getJSONArray(CRED_OPTIONS_KEY)
for (i in 0 until options.length()) {
val option = options.getJSONObject(i)
- val candidateBundle = convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY))
- candidateBundle.putParcelable(
+ val optionString = option.toString()
+ credentialOptionsFromHints[optionString]
+ ?.let { credentialOption ->
+ // if the current credential option was seen before, add the current
+ // viewNode to the credential option, but do not add it to the option list
+ // again. This will result in the same result as deduping based on
+ // traversed viewNode.
+ credentialOption.candidateQueryData.getParcelableArrayList(
+ CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId::class.java)
+ ?.let {
+ it.add(autofillId)
+ credentialOption.candidateQueryData.putParcelableArrayList(
+ CredentialProviderService.EXTRA_AUTOFILL_ID, it)
+ }
+ } ?: run {
+ val candidateBundle = convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY))
+ candidateBundle.putParcelableArrayList(
CredentialProviderService.EXTRA_AUTOFILL_ID,
- autofillId)
- credentialOptions.add(CredentialOption(
+ arrayListOf(autofillId))
+ val credentialOption = CredentialOption(
option.getString(TYPE_KEY),
convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)),
candidateBundle,
option.getBoolean(SYS_PROVIDER_REQ_KEY),
- ))
+ )
+ credentialOptions.add(credentialOption)
+ credentialOptionsFromHints[optionString] = credentialOption
+ }
}
return credentialOptions
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
index 6bd166e855ff..8c5c085d2880 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
@@ -19,5 +19,6 @@ package com.android.credentialmanager
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
+/** [Application] of credential selector. */
@HiltAndroidApp(Application::class)
class CredentialSelectorApp : Hilt_CredentialSelectorApp() \ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index bfe80c4c0b97..463c4d1b063e 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -17,6 +17,7 @@
package com.android.credentialmanager
import android.content.Intent
+import android.credentials.selection.BaseDialogResult
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.android.credentialmanager.CredentialSelectorUiState.Get
@@ -28,6 +29,10 @@ import com.android.credentialmanager.model.get.AuthenticationEntryInfo
import com.android.credentialmanager.model.get.CredentialEntryInfo
import com.android.credentialmanager.ui.mappers.toGet
import android.util.Log
+import com.android.credentialmanager.CredentialSelectorUiState.Cancel
+import com.android.credentialmanager.CredentialSelectorUiState.Close
+import com.android.credentialmanager.CredentialSelectorUiState.Create
+import com.android.credentialmanager.CredentialSelectorUiState.Idle
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -50,21 +55,21 @@ class CredentialSelectorViewModel @Inject constructor(
) { request, isPrimary, shouldClose ->
if (shouldClose) {
Log.d(TAG, "Request finished, closing ")
- return@combine CredentialSelectorUiState.Close
+ return@combine Close
}
when (request) {
- null -> CredentialSelectorUiState.Idle
- is Request.Cancel -> CredentialSelectorUiState.Cancel(request.appName)
- is Request.Close -> CredentialSelectorUiState.Close
- is Request.Create -> CredentialSelectorUiState.Create
+ null -> Idle
+ is Request.Cancel -> Cancel(request.appName)
+ is Request.Close -> Close
+ is Request.Create -> Create
is Request.Get -> request.toGet(isPrimary)
}
}
.stateIn(
viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
- initialValue = CredentialSelectorUiState.Idle,
+ initialValue = Idle,
)
fun updateRequest(intent: Intent) {
@@ -75,16 +80,14 @@ class CredentialSelectorViewModel @Inject constructor(
Log.d(TAG, "OnBackPressed")
when (uiState.value) {
is Get.MultipleEntry -> isPrimaryScreen.value = true
- else -> {
- shouldClose.value = true
- // TODO("b/300422310 - [Wear] Implement UI for cancellation request with message")
- }
+ is Create, Close, is Cancel, Idle -> shouldClose.value = true
+ is Get.SingleEntry, is Get.SingleEntryPerAccount -> cancel()
}
}
override fun cancel() {
+ credentialManagerClient.sendError(BaseDialogResult.RESULT_CODE_DIALOG_USER_CANCELED)
shouldClose.value = true
- // TODO("b/300422310 - [Wear] Implement UI for cancellation request with message")
}
override fun openSecondaryScreen() {
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
index 332b81634df7..f9a5887158eb 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -120,7 +120,6 @@ fun WearApp(
}
is CredentialSelectorUiState.Cancel -> {
- // TODO: b/300422310 - Implement cancel with message flow
onCloseApp()
}
diff --git a/packages/PackageInstaller/res/values-fr/strings.xml b/packages/PackageInstaller/res/values-fr/strings.xml
index 628b406e281b..531190327165 100644
--- a/packages/PackageInstaller/res/values-fr/strings.xml
+++ b/packages/PackageInstaller/res/values-fr/strings.xml
@@ -85,7 +85,7 @@
<string name="uninstalling_cloned_app" msgid="1826380164974984870">"Suppression du clone <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
<string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"Impossible de désinstaller une application d\'administration de l\'appareil active"</string>
<string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"Impossible de désinstaller une application d\'administration de l\'appareil active pour <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"Cette application nécessaire pour certains utilisateurs ou profils a été désinstallée pour d\'autres"</string>
+ <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"Cette application est requise pour certains utilisateurs ou profils et a été désinstallée pour d\'autres"</string>
<string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"Impossible de désinstaller l\'application, car elle est nécessaire pour votre profil."</string>
<string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"Impossible désinstaller appli, car elle est requise par administrateur appareil."</string>
<string name="manage_device_administrators" msgid="3092696419363842816">"Gérer les applis d\'administration de l\'appareil"</string>
diff --git a/packages/PackageInstaller/res/values-pt-rBR/strings.xml b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
index b96593e261d6..332517a775f4 100644
--- a/packages/PackageInstaller/res/values-pt-rBR/strings.xml
+++ b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
@@ -40,7 +40,7 @@
<string name="install_failed_msg" product="tv" msgid="1920009940048975221">"Não foi possível instalar o app <xliff:g id="APP_NAME">%1$s</xliff:g> na sua TV."</string>
<string name="install_failed_msg" product="default" msgid="6484461562647915707">"Não foi possível instalar o app <xliff:g id="APP_NAME">%1$s</xliff:g> no seu smartphone."</string>
<string name="launch" msgid="3952550563999890101">"Abrir"</string>
- <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"Seu administrador não permite a instalação de apps transferidos por download de fontes desconhecidas"</string>
+ <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"Seu administrador não permite a instalação de apps baixados de fontes desconhecidas"</string>
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Apps desconhecidos não podem ser instalados por este usuário"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este usuário não tem permissão para instalar apps"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
diff --git a/packages/PackageInstaller/res/values-pt/strings.xml b/packages/PackageInstaller/res/values-pt/strings.xml
index b96593e261d6..332517a775f4 100644
--- a/packages/PackageInstaller/res/values-pt/strings.xml
+++ b/packages/PackageInstaller/res/values-pt/strings.xml
@@ -40,7 +40,7 @@
<string name="install_failed_msg" product="tv" msgid="1920009940048975221">"Não foi possível instalar o app <xliff:g id="APP_NAME">%1$s</xliff:g> na sua TV."</string>
<string name="install_failed_msg" product="default" msgid="6484461562647915707">"Não foi possível instalar o app <xliff:g id="APP_NAME">%1$s</xliff:g> no seu smartphone."</string>
<string name="launch" msgid="3952550563999890101">"Abrir"</string>
- <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"Seu administrador não permite a instalação de apps transferidos por download de fontes desconhecidas"</string>
+ <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"Seu administrador não permite a instalação de apps baixados de fontes desconhecidas"</string>
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Apps desconhecidos não podem ser instalados por este usuário"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este usuário não tem permissão para instalar apps"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
diff --git a/packages/PrintRecommendationService/res/values/strings.xml b/packages/PrintRecommendationService/res/values/strings.xml
index 2bab1b65529b..b6c45b7a23c8 100644
--- a/packages/PrintRecommendationService/res/values/strings.xml
+++ b/packages/PrintRecommendationService/res/values/strings.xml
@@ -18,7 +18,6 @@
-->
<resources>
- <string name="plugin_vendor_google_cloud_print">Cloud Print</string>
<string name="plugin_vendor_hp">HP</string>
<string name="plugin_vendor_lexmark">Lexmark</string>
<string name="plugin_vendor_brother">Brother</string>
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
index 5a756fe50209..4ec88830386b 100644
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
@@ -23,7 +23,6 @@ import android.printservice.recommendation.RecommendationInfo;
import android.printservice.recommendation.RecommendationService;
import android.util.Log;
-import com.android.printservice.recommendation.plugin.google.CloudPrintPlugin;
import com.android.printservice.recommendation.plugin.hp.HPRecommendationPlugin;
import com.android.printservice.recommendation.plugin.mdnsFilter.MDNSFilterPlugin;
import com.android.printservice.recommendation.plugin.mdnsFilter.VendorConfig;
@@ -77,14 +76,6 @@ public class RecommendationServiceImpl extends RecommendationService
}
try {
- mPlugins.add(new RemotePrintServicePlugin(new CloudPrintPlugin(this), this,
- true));
- } catch (Exception e) {
- Log.e(LOG_TAG, "Could not initiate "
- + getString(R.string.plugin_vendor_google_cloud_print) + " plugin", e);
- }
-
- try {
mPlugins.add(new RemotePrintServicePlugin(new HPRecommendationPlugin(this), this,
false));
} catch (Exception e) {
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java
deleted file mode 100644
index 3029d10d4cf3..000000000000
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.printservice.recommendation.plugin.google;
-
-import static com.android.printservice.recommendation.util.MDNSUtils.ATTRIBUTE_TY;
-
-import android.content.Context;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.StringRes;
-
-import com.android.printservice.recommendation.PrintServicePlugin;
-import com.android.printservice.recommendation.R;
-import com.android.printservice.recommendation.util.MDNSFilteredDiscovery;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Plugin detecting <a href="https://developers.google.com/cloud-print/docs/privet">Google Cloud
- * Print</a> printers.
- */
-public class CloudPrintPlugin implements PrintServicePlugin {
- private static final String LOG_TAG = CloudPrintPlugin.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- private static final String ATTRIBUTE_TXTVERS = "txtvers";
- private static final String ATTRIBUTE_URL = "url";
- private static final String ATTRIBUTE_TYPE = "type";
- private static final String ATTRIBUTE_ID = "id";
- private static final String ATTRIBUTE_CS = "cs";
-
- private static final String TYPE = "printer";
-
- private static final String PRIVET_SERVICE = "_privet._tcp";
-
- /** The required mDNS service types */
- private static final Set<String> PRINTER_SERVICE_TYPE = Set.of(
- PRIVET_SERVICE); // Not checking _printer_._sub
-
- /** All possible connection states */
- private static final Set<String> POSSIBLE_CONNECTION_STATES = Set.of(
- "online",
- "offline",
- "connecting",
- "not-configured");
-
- private static final byte SUPPORTED_TXTVERS = '1';
-
- /** The mDNS filtered discovery */
- private final MDNSFilteredDiscovery mMDNSFilteredDiscovery;
-
- /**
- * Create a plugin detecting Google Cloud Print printers.
- *
- * @param context The context the plugin runs in
- */
- public CloudPrintPlugin(@NonNull Context context) {
- mMDNSFilteredDiscovery = new MDNSFilteredDiscovery(context, PRINTER_SERVICE_TYPE,
- nsdServiceInfo -> {
- // The attributes are case insensitive. For faster searching create a clone of
- // the map with the attribute-keys all in lower case.
- ArrayMap<String, byte[]> caseInsensitiveAttributes =
- new ArrayMap<>(nsdServiceInfo.getAttributes().size());
- for (Map.Entry<String, byte[]> entry : nsdServiceInfo.getAttributes()
- .entrySet()) {
- caseInsensitiveAttributes.put(entry.getKey().toLowerCase(),
- entry.getValue());
- }
-
- if (DEBUG) {
- Log.i(LOG_TAG, nsdServiceInfo.getServiceName() + ":");
- Log.i(LOG_TAG, "type: " + nsdServiceInfo.getServiceType());
- Log.i(LOG_TAG, "host: " + nsdServiceInfo.getHost());
- for (Map.Entry<String, byte[]> entry : caseInsensitiveAttributes.entrySet()) {
- if (entry.getValue() == null) {
- Log.i(LOG_TAG, entry.getKey() + "= null");
- } else {
- Log.i(LOG_TAG, entry.getKey() + "=" + new String(entry.getValue(),
- StandardCharsets.UTF_8));
- }
- }
- }
-
- byte[] txtvers = caseInsensitiveAttributes.get(ATTRIBUTE_TXTVERS);
- if (txtvers == null || txtvers.length != 1 || txtvers[0] != SUPPORTED_TXTVERS) {
- // The spec requires this to be the first attribute, but at this time we
- // lost the order of the attributes
- return false;
- }
-
- if (caseInsensitiveAttributes.get(ATTRIBUTE_TY) == null) {
- return false;
- }
-
- byte[] url = caseInsensitiveAttributes.get(ATTRIBUTE_URL);
- if (url == null || url.length == 0) {
- return false;
- }
-
- byte[] type = caseInsensitiveAttributes.get(ATTRIBUTE_TYPE);
- if (type == null || !TYPE.equals(
- new String(type, StandardCharsets.UTF_8).toLowerCase())) {
- return false;
- }
-
- if (caseInsensitiveAttributes.get(ATTRIBUTE_ID) == null) {
- return false;
- }
-
- byte[] cs = caseInsensitiveAttributes.get(ATTRIBUTE_CS);
- if (cs == null || !POSSIBLE_CONNECTION_STATES.contains(
- new String(cs, StandardCharsets.UTF_8).toLowerCase())) {
- return false;
- }
-
- InetAddress address = nsdServiceInfo.getHost();
- if (!(address instanceof Inet4Address)) {
- // Not checking for link local address
- return false;
- }
-
- return true;
- });
- }
-
- @Override
- @NonNull public CharSequence getPackageName() {
- return "com.google.android.apps.cloudprint";
- }
-
- @Override
- public void start(@NonNull PrinterDiscoveryCallback callback) throws Exception {
- mMDNSFilteredDiscovery.start(callback);
- }
-
- @Override
- @StringRes public int getName() {
- return R.string.plugin_vendor_google_cloud_print;
- }
-
- @Override
- public void stop() throws Exception {
- mMDNSFilteredDiscovery.stop();
- }
-}
diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
index 5b39f4ee1541..18e8fc38ddb0 100644
--- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
@@ -219,7 +219,6 @@ public class RestrictedLockUtils {
}
}
-
/**
* Shows restricted setting dialog.
*
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 460a6f7261ae..761bb7918afd 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -27,8 +27,8 @@ import com.android.settingslib.spa.gallery.chart.ChartPageProvider
import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
import com.android.settingslib.spa.gallery.dialog.NavDialogProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
-import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuBoxPageProvider
-import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuCheckBoxProvider
+import com.android.settingslib.spa.gallery.editor.SettingsDropdownBoxPageProvider
+import com.android.settingslib.spa.gallery.editor.SettingsDropdownCheckBoxProvider
import com.android.settingslib.spa.gallery.home.HomePageProvider
import com.android.settingslib.spa.gallery.itemList.ItemListPageProvider
import com.android.settingslib.spa.gallery.itemList.ItemOperatePageProvider
@@ -99,8 +99,8 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
OperateListPageProvider,
EditorMainPageProvider,
SettingsOutlinedTextFieldPageProvider,
- SettingsExposedDropdownMenuBoxPageProvider,
- SettingsExposedDropdownMenuCheckBoxProvider,
+ SettingsDropdownBoxPageProvider,
+ SettingsDropdownCheckBoxProvider,
SettingsTextFieldPasswordPageProvider,
SearchScaffoldPageProvider,
SuwScaffoldPageProvider,
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
index d5cf1a35b4df..79c5ebbce5fa 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
@@ -88,11 +88,13 @@ object CardPageProvider : SettingsPageProvider {
@Composable
private fun SettingsCardWithoutIcon() {
+ val sampleTitle = stringResource(R.string.sample_title)
+ var title by remember { mutableStateOf(sampleTitle) }
SettingsCard(
CardModel(
- title = stringResource(R.string.sample_title),
+ title = title,
text = stringResource(R.string.sample_text),
- )
+ ) { title = "Clicked" }
)
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
index 4875ea99d4d6..c511542f265a 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
@@ -35,9 +35,9 @@ object EditorMainPageProvider : SettingsPageProvider {
return listOf(
SettingsOutlinedTextFieldPageProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
- SettingsExposedDropdownMenuBoxPageProvider.buildInjectEntry().setLink(fromPage = owner)
+ SettingsDropdownBoxPageProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
- SettingsExposedDropdownMenuCheckBoxProvider.buildInjectEntry().setLink(fromPage = owner)
+ SettingsDropdownCheckBoxProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
SettingsTextFieldPasswordPageProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownBoxPageProvider.kt
index 5ffbe8ba8a26..2ebb5f5eba27 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownBoxPageProvider.kt
@@ -28,16 +28,15 @@ import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
-import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuBox
+import com.android.settingslib.spa.widget.editor.SettingsDropdownBox
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
-private const val TITLE = "Sample SettingsExposedDropdownMenuBox"
+private const val TITLE = "Sample SettingsDropdownBox"
-object SettingsExposedDropdownMenuBoxPageProvider : SettingsPageProvider {
- override val name = "SettingsExposedDropdownMenuBox"
- private const val exposedDropdownMenuBoxLabel = "ExposedDropdownMenuBoxLabel"
+object SettingsDropdownBoxPageProvider : SettingsPageProvider {
+ override val name = "SettingsDropdownBox"
override fun getTitle(arguments: Bundle?): String {
return TITLE
@@ -45,18 +44,44 @@ object SettingsExposedDropdownMenuBoxPageProvider : SettingsPageProvider {
@Composable
override fun Page(arguments: Bundle?) {
- var selectedItem by remember { mutableIntStateOf(-1) }
- val options = listOf("item1", "item2", "item3")
RegularScaffold(title = TITLE) {
- SettingsExposedDropdownMenuBox(
- label = exposedDropdownMenuBoxLabel,
- options = options,
- selectedOptionIndex = selectedItem,
- enabled = true,
- onselectedOptionTextChange = { selectedItem = it })
+ Regular()
+ NotEnabled()
+ Empty()
}
}
+ @Composable
+ private fun Regular() {
+ var selectedItem by remember { mutableIntStateOf(-1) }
+ SettingsDropdownBox(
+ label = "SettingsDropdownBox",
+ options = listOf("item1", "item2", "item3"),
+ selectedOptionIndex = selectedItem,
+ ) { selectedItem = it }
+ }
+
+ @Composable
+ private fun NotEnabled() {
+ var selectedItem by remember { mutableIntStateOf(0) }
+ SettingsDropdownBox(
+ label = "Not enabled",
+ options = listOf("item1", "item2", "item3"),
+ enabled = false,
+ selectedOptionIndex = selectedItem,
+ ) { selectedItem = it }
+ }
+
+ @Composable
+ private fun Empty() {
+ var selectedItem by remember { mutableIntStateOf(-1) }
+ SettingsDropdownBox(
+ label = "Empty",
+ options = emptyList(),
+ selectedOptionIndex = selectedItem,
+ ) { selectedItem = it }
+ }
+
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = createSettingsPage())
.setUiLayoutFn {
@@ -70,8 +95,8 @@ object SettingsExposedDropdownMenuBoxPageProvider : SettingsPageProvider {
@Preview(showBackground = true)
@Composable
-private fun SettingsExposedDropdownMenuBoxPagePreview() {
+private fun SettingsDropdownBoxPagePreview() {
SettingsTheme {
- SettingsExposedDropdownMenuBoxPageProvider.Page(null)
+ SettingsDropdownBoxPageProvider.Page(null)
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownCheckBoxProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownCheckBoxProvider.kt
new file mode 100644
index 000000000000..33ab75d6189d
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownCheckBoxProvider.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.gallery.editor
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckBox
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+private const val TITLE = "Sample SettingsDropdownCheckBox"
+
+object SettingsDropdownCheckBoxProvider : SettingsPageProvider {
+ override val name = "SettingsDropdownCheckBox"
+
+ override fun getTitle(arguments: Bundle?): String {
+ return TITLE
+ }
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ RegularScaffold(title = TITLE) {
+ SettingsDropdownCheckBox(
+ label = "SettingsDropdownCheckBox",
+ options = remember {
+ listOf(
+ SettingsDropdownCheckOption("Item 1"),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption("Item 3"),
+ )
+ },
+ )
+ SettingsDropdownCheckBox(
+ label = "Empty list",
+ options = emptyList(),
+ )
+ SettingsDropdownCheckBox(
+ label = "Disabled",
+ options = remember {
+ listOf(
+ SettingsDropdownCheckOption("Item 1", selected = mutableStateOf(true)),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption("Item 3"),
+ )
+ },
+ enabled = false,
+ )
+ SettingsDropdownCheckBox(
+ label = "With disabled item",
+ options = remember {
+ listOf(
+ SettingsDropdownCheckOption("Item 1"),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption(
+ text = "Disabled item 1",
+ changeable = false,
+ selected = mutableStateOf(true),
+ ),
+ SettingsDropdownCheckOption("Disabled item 2", changeable = false),
+ )
+ },
+ )
+ SettingsDropdownCheckBox(
+ label = "With select all",
+ options = remember {
+ listOf(
+ SettingsDropdownCheckOption("All", isSelectAll = true),
+ SettingsDropdownCheckOption("Item 1"),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption("Item 3"),
+ )
+ },
+ )
+ SettingsDropdownCheckBox(
+ label = "With disabled item and select all",
+ options =
+ remember {
+ listOf(
+ SettingsDropdownCheckOption("All", isSelectAll = true, changeable = false),
+ SettingsDropdownCheckOption("Item 1"),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption(
+ text = "Disabled item 1",
+ changeable = false,
+ selected = mutableStateOf(true),
+ ),
+ SettingsDropdownCheckOption("Disabled item 2", changeable = false),
+ )
+ },
+ )
+ }
+ }
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner = createSettingsPage()).setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SettingsDropdownCheckBoxPagePreview() {
+ SettingsTheme {
+ SettingsDropdownCheckBoxProvider.Page(null)
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt
deleted file mode 100644
index d28964676bdd..000000000000
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.settingslib.spa.gallery.editor
-
-import android.os.Bundle
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.tooling.preview.Preview
-import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
-import com.android.settingslib.spa.framework.common.SettingsPageProvider
-import com.android.settingslib.spa.framework.common.createSettingsPage
-import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.theme.SettingsTheme
-import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuCheckBox
-import com.android.settingslib.spa.widget.preference.Preference
-import com.android.settingslib.spa.widget.preference.PreferenceModel
-import com.android.settingslib.spa.widget.scaffold.RegularScaffold
-
-private const val TITLE = "Sample SettingsExposedDropdownMenuCheckBox"
-
-object SettingsExposedDropdownMenuCheckBoxProvider : SettingsPageProvider {
- override val name = "SettingsExposedDropdownMenuCheckBox"
- private const val exposedDropdownMenuCheckBoxLabel = "ExposedDropdownMenuCheckBoxLabel"
- private val options = listOf("item1", "item2", "item3")
- private val selectedOptionsState1 = mutableStateListOf(0, 1)
-
- override fun getTitle(arguments: Bundle?): String {
- return TITLE
- }
-
- @Composable
- override fun Page(arguments: Bundle?) {
- RegularScaffold(title = TITLE) {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- onSelectedOptionStateChange = {},
- )
- }
- }
-
- fun buildInjectEntry(): SettingsEntryBuilder {
- return SettingsEntryBuilder.createInject(owner = createSettingsPage()).setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = TITLE
- override val onClick = navigator(name)
- })
- }
- }
-}
-
-@Preview(showBackground = true)
-@Composable
-private fun SettingsExposedDropdownMenuCheckBoxPagePreview() {
- SettingsTheme {
- SettingsExposedDropdownMenuCheckBoxProvider.Page(null)
- }
-} \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
index 960ebccf6c25..8100fd58f5a0 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
@@ -45,4 +45,6 @@ data class CardModel(
/** If specified, this color will be used to tint the icon and the buttons. */
val containerColor: Color = Color.Unspecified,
+
+ val onClick: (() -> Unit)? = null,
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
index 700fa487ed73..621825a82c1e 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.spa.widget.card
import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
@@ -102,10 +103,11 @@ internal fun SettingsCardImpl(model: CardModel) {
AnimatedVisibility(visible = model.isVisible()) {
SettingsCardContent(containerColor = model.containerColor) {
Column(
- modifier = Modifier.padding(
- horizontal = SettingsDimension.dialogItemPaddingHorizontal,
- vertical = SettingsDimension.itemPaddingAround,
- ),
+ modifier = (model.onClick?.let { Modifier.clickable(onClick = it) } ?: Modifier)
+ .padding(
+ horizontal = SettingsDimension.dialogItemPaddingHorizontal,
+ vertical = SettingsDimension.itemPaddingAround,
+ ),
verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
) {
CardHeader(model.imageVector, model.tintColor, model.onDismiss)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/DropdownTextBox.kt
index f6692a356899..679c562ac92d 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/DropdownTextBox.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@ package com.android.settingslib.spa.widget.editor
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
-import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
@@ -31,80 +30,58 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.theme.SettingsDimension
-import com.android.settingslib.spa.framework.theme.SettingsTheme
-@Composable
+internal interface DropdownTextBoxScope {
+ fun dismiss()
+}
+
@OptIn(ExperimentalMaterial3Api::class)
-fun SettingsExposedDropdownMenuBox(
+@Composable
+internal fun DropdownTextBox(
label: String,
- options: List<String>,
- selectedOptionIndex: Int,
- enabled: Boolean,
- onselectedOptionTextChange: (Int) -> Unit,
+ text: String,
+ enabled: Boolean = true,
+ errorMessage: String? = null,
+ content: @Composable DropdownTextBoxScope.() -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
+ val scope = remember {
+ object : DropdownTextBoxScope {
+ override fun dismiss() {
+ expanded = false
+ }
+ }
+ }
ExposedDropdownMenuBox(
expanded = expanded,
- onExpandedChange = { expanded = it },
+ onExpandedChange = { expanded = enabled && it },
modifier = Modifier
- .width(350.dp)
- .padding(SettingsDimension.menuFieldPadding),
+ .padding(SettingsDimension.menuFieldPadding)
+ .width(Width),
) {
OutlinedTextField(
// The `menuAnchor` modifier must be passed to the text field for correctness.
modifier = Modifier
.menuAnchor()
.fillMaxWidth(),
- value = options.getOrElse(selectedOptionIndex) { "" },
+ value = text,
onValueChange = { },
label = { Text(text = label) },
- trailingIcon = {
- ExposedDropdownMenuDefaults.TrailingIcon(
- expanded = expanded
- )
- },
+ trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
singleLine = true,
readOnly = true,
- enabled = enabled
+ enabled = enabled,
+ isError = errorMessage != null,
+ supportingText = errorMessage?.let { { Text(text = it) } },
)
- if (options.isNotEmpty()) {
- ExposedDropdownMenu(
- expanded = expanded,
- modifier = Modifier
- .fillMaxWidth(),
- onDismissRequest = { expanded = false },
- ) {
- options.forEach { option ->
- DropdownMenuItem(
- text = { Text(option) },
- onClick = {
- onselectedOptionTextChange(options.indexOf(option))
- expanded = false
- },
- contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
- )
- }
- }
- }
+ ExposedDropdownMenu(
+ expanded = expanded,
+ modifier = Modifier.width(Width),
+ onDismissRequest = { expanded = false },
+ ) { scope.content() }
}
}
-@Preview
-@Composable
-private fun SettingsExposedDropdownMenuBoxsPreview() {
- val item1 = "item1"
- val item2 = "item2"
- val item3 = "item3"
- val options = listOf(item1, item2, item3)
- SettingsTheme {
- SettingsExposedDropdownMenuBox(
- label = "ExposedDropdownMenuBoxLabel",
- options = options,
- selectedOptionIndex = 0,
- enabled = true,
- onselectedOptionTextChange = {})
- }
-} \ No newline at end of file
+private val Width = 310.dp
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBox.kt
new file mode 100644
index 000000000000..ff141c2b383c
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBox.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.editor
+
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+@Composable
+@OptIn(ExperimentalMaterial3Api::class)
+fun SettingsDropdownBox(
+ label: String,
+ options: List<String>,
+ selectedOptionIndex: Int,
+ enabled: Boolean = true,
+ onSelectedOptionChange: (Int) -> Unit,
+) {
+ DropdownTextBox(
+ label = label,
+ text = options.getOrElse(selectedOptionIndex) { "" },
+ enabled = enabled && options.isNotEmpty(),
+ ) {
+ options.forEachIndexed { index, option ->
+ DropdownMenuItem(
+ text = { Text(option) },
+ onClick = {
+ dismiss()
+ onSelectedOptionChange(index)
+ },
+ contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun SettingsDropdownBoxPreview() {
+ val item1 = "item1"
+ val item2 = "item2"
+ val item3 = "item3"
+ val options = listOf(item1, item2, item3)
+ SettingsTheme {
+ SettingsDropdownBox(
+ label = "ExposedDropdownMenuBoxLabel",
+ options = options,
+ selectedOptionIndex = 0,
+ enabled = true,
+ ) {}
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBox.kt
new file mode 100644
index 000000000000..0e7e49960be1
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBox.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.settingslib.spa.widget.editor
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsOpacity.alphaForEnabled
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption.Companion.changeable
+
+data class SettingsDropdownCheckOption(
+ /** The displayed text of this option. */
+ val text: String,
+
+ /** If true, check / uncheck this item will check / uncheck all enabled options. */
+ val isSelectAll: Boolean = false,
+
+ /** If not changeable, cannot check or uncheck this option. */
+ val changeable: Boolean = true,
+
+ /** The selected state of this option. */
+ val selected: MutableState<Boolean> = mutableStateOf(false),
+
+ /** Get called when the option is clicked, no matter if it's changeable. */
+ val onClick: () -> Unit = {},
+) {
+ companion object {
+ val List<SettingsDropdownCheckOption>.changeable: Boolean
+ get() = filter { !it.isSelectAll }.any { it.changeable }
+ }
+}
+
+@Composable
+fun SettingsDropdownCheckBox(
+ label: String,
+ options: List<SettingsDropdownCheckOption>,
+ emptyText: String = "",
+ enabled: Boolean = true,
+ errorMessage: String? = null,
+ onSelectedStateChange: () -> Unit = {},
+) {
+ DropdownTextBox(
+ label = label,
+ text = getDisplayText(options) ?: emptyText,
+ enabled = enabled && options.changeable,
+ errorMessage = errorMessage,
+ ) {
+ for (option in options) {
+ CheckboxItem(option) {
+ option.onClick()
+ if (option.changeable) {
+ checkboxItemOnClick(options, option)
+ onSelectedStateChange()
+ }
+ }
+ }
+ }
+}
+
+private fun getDisplayText(options: List<SettingsDropdownCheckOption>): String? {
+ val selectedOptions = options.filter { it.selected.value }
+ if (selectedOptions.isEmpty()) return null
+ return selectedOptions.filter { it.isSelectAll }.ifEmpty { selectedOptions }
+ .joinToString { it.text }
+}
+
+private fun checkboxItemOnClick(
+ options: List<SettingsDropdownCheckOption>,
+ clickedOption: SettingsDropdownCheckOption,
+) {
+ if (!clickedOption.changeable) return
+ val newChecked = !clickedOption.selected.value
+ if (clickedOption.isSelectAll) {
+ for (option in options.filter { it.changeable }) option.selected.value = newChecked
+ } else {
+ clickedOption.selected.value = newChecked
+ }
+ val (selectAllOptions, regularOptions) = options.partition { it.isSelectAll }
+ val isAllRegularOptionsChecked = regularOptions.all { it.selected.value }
+ selectAllOptions.forEach { it.selected.value = isAllRegularOptionsChecked }
+}
+
+@Composable
+private fun CheckboxItem(
+ option: SettingsDropdownCheckOption,
+ onClick: (SettingsDropdownCheckOption) -> Unit,
+) {
+ TextButton(
+ onClick = { onClick(option) },
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Checkbox(
+ checked = option.selected.value,
+ onCheckedChange = null,
+ enabled = option.changeable,
+ )
+ Text(text = option.text, modifier = Modifier.alphaForEnabled(option.changeable))
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun ActionButtonsPreview() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ val item3 = SettingsDropdownCheckOption("item3")
+ val options = listOf(item1, item2, item3)
+ SettingsTheme {
+ SettingsDropdownCheckBox(
+ label = "label",
+ options = options,
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
deleted file mode 100644
index e704505117cf..000000000000
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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.settingslib.spa.widget.editor
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.material3.Checkbox
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.ExposedDropdownMenuBox
-import androidx.compose.material3.ExposedDropdownMenuDefaults
-import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.runtime.snapshots.SnapshotStateList
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import com.android.settingslib.spa.framework.theme.SettingsDimension
-import com.android.settingslib.spa.framework.theme.SettingsTheme
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-fun SettingsExposedDropdownMenuCheckBox(
- label: String,
- options: List<String>,
- selectedOptionsState: SnapshotStateList<Int>,
- emptyVal: String = "",
- enabled: Boolean,
- errorMessage: String? = null,
- onSelectedOptionStateChange: () -> Unit,
-) {
- var dropDownWidth by remember { mutableIntStateOf(0) }
- var expanded by remember { mutableStateOf(false) }
- val allIndex = options.indexOf("*")
- ExposedDropdownMenuBox(
- expanded = expanded,
- onExpandedChange = { expanded = it },
- modifier = Modifier
- .width(350.dp)
- .padding(SettingsDimension.textFieldPadding)
- .onSizeChanged { dropDownWidth = it.width },
- ) {
- OutlinedTextField(
- // The `menuAnchor` modifier must be passed to the text field for correctness.
- modifier = Modifier
- .menuAnchor()
- .fillMaxWidth(),
- value = if (selectedOptionsState.size == 0) emptyVal
- else if (selectedOptionsState.contains(allIndex)) "*"
- else selectedOptionsState.joinToString { options[it] },
- onValueChange = {},
- label = { Text(text = label) },
- trailingIcon = {
- ExposedDropdownMenuDefaults.TrailingIcon(
- expanded = expanded
- )
- },
- readOnly = true,
- enabled = enabled,
- isError = errorMessage != null,
- supportingText = {
- if (errorMessage != null) {
- Text(text = errorMessage)
- }
- }
- )
- if (options.isNotEmpty()) {
- ExposedDropdownMenu(
- expanded = expanded,
- modifier = Modifier.width(with(LocalDensity.current) { dropDownWidth.toDp() }),
- onDismissRequest = { expanded = false },
- ) {
- options.forEachIndexed { index, option ->
- CheckboxItem(
- selectedOptionsState,
- index,
- allIndex,
- onSelectedOptionStateChange,
- option,
- )
- }
- }
- }
- }
-}
-
-@Composable
-private fun CheckboxItem(
- selectedOptionsState: SnapshotStateList<Int>,
- index: Int,
- allIndex: Int,
- onSelectedOptionStateChange: () -> Unit,
- option: String
-) {
- TextButton(
- modifier = Modifier.fillMaxWidth(),
- onClick = {
- if (selectedOptionsState.contains(index)) {
- if (index == allIndex) {
- selectedOptionsState.clear()
- } else {
- selectedOptionsState.remove(index)
- selectedOptionsState.remove(allIndex)
- }
- } else {
- selectedOptionsState.add(index)
- }
- onSelectedOptionStateChange()
- }) {
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround),
- verticalAlignment = Alignment.CenterVertically
- ) {
- Checkbox(
- checked = selectedOptionsState.contains(index),
- onCheckedChange = null,
- )
- Text(text = option)
- }
- }
-}
-
-@Preview
-@Composable
-private fun ActionButtonsPreview() {
- val options = listOf("item1", "item2", "item3")
- val selectedOptionsState = remember { mutableStateListOf(0, 1) }
- SettingsTheme {
- SettingsExposedDropdownMenuCheckBox(
- label = "label",
- options = options,
- selectedOptionsState = selectedOptionsState,
- enabled = true,
- onSelectedOptionStateChange = {})
- }
-}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
index b5b2525bffdd..ffc7e86665dd 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
@@ -141,6 +141,23 @@ class SettingsCardTest {
composeTestRule.onNodeWithText(TEXT).isNotDisplayed()
}
+ @Test
+ fun settingsCard_clickable() {
+ var clicked by mutableStateOf(false)
+ composeTestRule.setContent {
+ SettingsCard(
+ CardModel(
+ title = TITLE,
+ text = "",
+ ) { clicked = true }
+ )
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+
+ assertThat(clicked).isTrue()
+ }
+
private companion object {
const val TITLE = "Title"
const val TEXT = "Text"
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBoxTest.kt
new file mode 100644
index 000000000000..c34742461774
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBoxTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.editor
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsDropdownBoxTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun dropdownMenuBox_displayed() {
+ composeTestRule.setContent {
+ var selectedItem by remember { mutableStateOf(0) }
+ SettingsDropdownBox(
+ label = LABEL,
+ options = options,
+ selectedOptionIndex = selectedItem,
+ ) { selectedItem = it }
+ }
+
+ composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
+ }
+
+ @Test
+ fun dropdownMenuBox_enabled_expanded() {
+ composeTestRule.setContent {
+ var selectedItem by remember { mutableIntStateOf(0) }
+ SettingsDropdownBox(
+ label = LABEL,
+ options = options,
+ selectedOptionIndex = selectedItem
+ ) { selectedItem = it }
+ }
+ composeTestRule.onNodeWithText(ITEM2).assertDoesNotExist()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+
+ composeTestRule.onNodeWithText(ITEM2).assertIsDisplayed()
+ }
+
+ @Test
+ fun dropdownMenuBox_notEnabled_notExpanded() {
+ composeTestRule.setContent {
+ var selectedItem by remember { mutableIntStateOf(0) }
+ SettingsDropdownBox(
+ label = LABEL,
+ options = options,
+ enabled = false,
+ selectedOptionIndex = selectedItem
+ ) { selectedItem = it }
+ }
+ composeTestRule.onNodeWithText(ITEM2).assertDoesNotExist()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+
+ composeTestRule.onNodeWithText(ITEM2).assertDoesNotExist()
+ }
+
+ @Test
+ fun dropdownMenuBox_valueChanged() {
+ composeTestRule.setContent {
+ var selectedItem by remember { mutableIntStateOf(0) }
+ SettingsDropdownBox(
+ label = LABEL,
+ options = options,
+ selectedOptionIndex = selectedItem
+ ) { selectedItem = it }
+ }
+ composeTestRule.onNodeWithText(ITEM2).assertDoesNotExist()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+ composeTestRule.onNodeWithText(ITEM2).performClick()
+
+ composeTestRule.onNodeWithText(ITEM2).assertIsDisplayed()
+ }
+ private companion object {
+ const val LABEL = "Label"
+ const val ITEM2 = "item2"
+ val options = listOf("item1", ITEM2, "item3")
+ }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBoxTest.kt
new file mode 100644
index 000000000000..72b7b98c2a94
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBoxTest.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.editor
+
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.hasAnyAncestor
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.isPopup
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.hasRole
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsDropdownCheckBoxTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun dropdownCheckBox_displayed() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ val item3 = SettingsDropdownCheckOption("item3")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(item1, item2, item3),
+ )
+ }
+
+ composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
+ }
+
+ @Test
+ fun dropdownCheckBox_expanded() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ val item3 = SettingsDropdownCheckOption("item3")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(item1, item2, item3),
+ )
+ }
+ composeTestRule.onOption(item3).assertDoesNotExist()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+
+ composeTestRule.onOption(item3).assertIsDisplayed()
+ }
+
+ @Test
+ fun dropdownCheckBox_valueAdded() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ val item3 = SettingsDropdownCheckOption("item3")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(item1, item2, item3),
+ )
+ }
+ composeTestRule.onDropdownBox(item3.text).assertDoesNotExist()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+ composeTestRule.onOption(item3).performClick()
+
+ composeTestRule.onDropdownBox(item3.text).assertIsDisplayed()
+ assertThat(item3.selected.value).isTrue()
+ }
+
+ @Test
+ fun dropdownCheckBox_valueDeleted() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2", selected = mutableStateOf(true))
+ val item3 = SettingsDropdownCheckOption("item3")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(item1, item2, item3),
+ )
+ }
+ composeTestRule.onDropdownBox(item2.text).assertIsDisplayed()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+ composeTestRule.onOption(item2).performClick()
+
+ composeTestRule.onDropdownBox(item2.text).assertDoesNotExist()
+ assertThat(item2.selected.value).isFalse()
+ }
+
+ @Test
+ fun dropdownCheckBox_withSelectAll() {
+ val selectAll = SettingsDropdownCheckOption("All", isSelectAll = true)
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(selectAll, item1, item2),
+ )
+ }
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+ composeTestRule.onOption(selectAll).performClick()
+
+ composeTestRule.onDropdownBox(selectAll.text).assertIsDisplayed()
+ composeTestRule.onDropdownBox(item1.text).assertDoesNotExist()
+ composeTestRule.onDropdownBox(item2.text).assertDoesNotExist()
+ assertThat(item1.selected.value).isTrue()
+ assertThat(item2.selected.value).isTrue()
+ }
+
+ private companion object {
+ const val LABEL = "Label"
+ }
+}
+
+private fun ComposeContentTestRule.onDropdownBox(text: String) =
+ onNode(hasRole(Role.DropdownList) and hasText(text))
+
+private fun ComposeContentTestRule.onOption(option: SettingsDropdownCheckOption) =
+ onNode(hasAnyAncestor(isPopup()) and hasText(option.text))
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt
deleted file mode 100644
index bc67e4c61ea5..000000000000
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.settingslib.spa.widget.editor
-
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithText
-import androidx.compose.ui.test.performClick
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class SettingsExposedDropdownMenuBoxTest {
- @get:Rule
- val composeTestRule = createComposeRule()
- private val options = listOf("item1", "item2", "item3")
- private val item2 = "item2"
- private val exposedDropdownMenuBoxLabel = "ExposedDropdownMenuBoxLabel"
-
- @Test
- fun exposedDropdownMenuBoxs_displayed() {
- composeTestRule.setContent {
- var selectedItem by remember { mutableStateOf(0) }
- SettingsExposedDropdownMenuBox(
- label = exposedDropdownMenuBoxLabel,
- options = options,
- selectedOptionIndex = selectedItem,
- enabled = true,
- onselectedOptionTextChange = { selectedItem = it })
- }
- composeTestRule.onNodeWithText(exposedDropdownMenuBoxLabel, substring = true)
- .assertIsDisplayed()
- }
-
- @Test
- fun exposedDropdownMenuBoxs_expanded() {
- composeTestRule.setContent {
- var selectedItem by remember { mutableIntStateOf(0) }
- SettingsExposedDropdownMenuBox(
- label = exposedDropdownMenuBoxLabel,
- options = options,
- selectedOptionIndex = selectedItem,
- enabled = true,
- onselectedOptionTextChange = { selectedItem = it })
- }
- composeTestRule.onNodeWithText(item2, substring = true)
- .assertDoesNotExist()
- composeTestRule.onNodeWithText(exposedDropdownMenuBoxLabel, substring = true)
- .performClick()
- composeTestRule.onNodeWithText(item2, substring = true)
- .assertIsDisplayed()
- }
-
- @Test
- fun exposedDropdownMenuBoxs_valueChanged() {
- composeTestRule.setContent {
- var selectedItem by remember { mutableIntStateOf(0) }
- SettingsExposedDropdownMenuBox(
- label = exposedDropdownMenuBoxLabel,
- options = options,
- selectedOptionIndex = selectedItem,
- enabled = true,
- onselectedOptionTextChange = { selectedItem = it })
- }
- composeTestRule.onNodeWithText(item2, substring = true)
- .assertDoesNotExist()
- composeTestRule.onNodeWithText(exposedDropdownMenuBoxLabel, substring = true)
- .performClick()
- composeTestRule.onNodeWithText(item2, substring = true)
- .performClick()
- composeTestRule.onNodeWithText(item2, substring = true)
- .assertIsDisplayed()
- }
-} \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt
deleted file mode 100644
index 2b78ed7d6fa2..000000000000
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.settingslib.spa.widget.editor
-
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.test.SemanticsNodeInteraction
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.hasText
-import androidx.compose.ui.test.isFocused
-import androidx.compose.ui.test.junit4.ComposeContentTestRule
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithText
-import androidx.compose.ui.test.performClick
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class SettingsExposedDropdownMenuCheckBoxTest {
- @get:Rule
- val composeTestRule = createComposeRule()
- private val item1 = "item1"
- private val item2 = "item2"
- private val item3 = "item3"
- private val options = listOf(item1, item2, item3)
- private val selectedOptionsState1 = mutableStateListOf(0, 1)
- private val exposedDropdownMenuCheckBoxLabel = "ExposedDropdownMenuCheckBoxLabel"
-
- @Test
- fun exposedDropdownMenuCheckBox_displayed() {
- composeTestRule.setContent {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- ) {}
- }
- composeTestRule.onNodeWithText(
- exposedDropdownMenuCheckBoxLabel, substring = true
- ).assertIsDisplayed()
- }
-
- @Test
- fun exposedDropdownMenuCheckBox_expanded() {
- composeTestRule.setContent {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- ) {}
- }
- composeTestRule.onNodeWithText(item3, substring = true).assertDoesNotExist()
- composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
- .performClick()
- composeTestRule.onNodeWithText(item3, substring = true).assertIsDisplayed()
- }
-
- @Test
- fun exposedDropdownMenuCheckBox_valueAdded() {
- composeTestRule.setContent {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- ) {}
- }
- composeTestRule.onNodeWithText(item3, substring = true).assertDoesNotExist()
- composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
- .performClick()
- composeTestRule.onNodeWithText(item3, substring = true).performClick()
- composeTestRule.onFocusedText(item3).assertIsDisplayed()
- }
-
- @Test
- fun exposedDropdownMenuCheckBox_valueDeleted() {
- composeTestRule.setContent {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- ) {}
- }
- composeTestRule.onNodeWithText(item2, substring = true).assertIsDisplayed()
- composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
- .performClick()
- composeTestRule.onNotFocusedText(item2).performClick()
- composeTestRule.onFocusedText(item2).assertDoesNotExist()
- }
-}
-
-fun ComposeContentTestRule.onFocusedText(text: String): SemanticsNodeInteraction =
- onNode(isFocused() and hasText(text, substring = true))
-
-fun ComposeContentTestRule.onNotFocusedText(text: String): SemanticsNodeInteraction =
- onNode(!isFocused() and hasText(text, substring = true)) \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/SemanticsMatcher.kt b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/SemanticsMatcher.kt
new file mode 100644
index 000000000000..856bed603354
--- /dev/null
+++ b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/SemanticsMatcher.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.testutils
+
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.getOrNull
+import androidx.compose.ui.test.SemanticsMatcher
+
+fun hasRole(role: Role) = SemanticsMatcher("${SemanticsProperties.Role.name} has $role") {
+ it.config.getOrNull(SemanticsProperties.Role) == role
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
index 9432d5995151..6b1893c73b3f 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
@@ -31,7 +31,6 @@ import kotlinx.coroutines.flow.flowOn
data class EnhancedConfirmation(
val key: String,
- val uid: Int,
val packageName: String,
)
data class Restrictions(
@@ -91,7 +90,7 @@ internal class RestrictionsProviderImpl(
restrictions.enhancedConfirmation?.let { ec ->
RestrictedLockUtilsInternal
.checkIfRequiresEnhancedConfirmation(context, ec.key,
- ec.uid, ec.packageName)
+ ec.packageName)
?.let { intent -> return BlockedByEcmImpl(context = context, intent = intent) }
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index 74b556ea106e..27e00c05c33f 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -159,7 +159,6 @@ internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionApp
keys = switchRestrictionKeys,
enhancedConfirmation = enhancedConfirmationKey?.let { EnhancedConfirmation(
key = it,
- uid = checkNotNull(applicationInfo).uid,
packageName = packageName) })
RestrictedSwitchPreference(switchModel, restrictions, restrictionsProviderFactory)
InfoPageAdditionalContent(record, isAllowed)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index 4b474379c54b..2e8b76a03722 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -150,15 +150,13 @@ internal class TogglePermissionInternalAppListModel<T : AppRecord>(
@Composable
fun getSummary(record: T): () -> String {
- val restrictions = remember(record.app.userId,
- record.app.uid, record.app.packageName) {
+ val restrictions = remember(record.app.userId, record.app.packageName) {
Restrictions(
userId = record.app.userId,
keys = listModel.switchRestrictionKeys,
enhancedConfirmation = listModel.enhancedConfirmationKey?.let {
EnhancedConfirmation(
key = it,
- uid = record.app.uid,
packageName = record.app.packageName)
})
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
index 00ba9b4d9836..b88d1c5760a6 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
@@ -32,6 +32,7 @@ import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByAdmin
+import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByEcm
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -44,6 +45,7 @@ class RestrictedSwitchPreferenceTest {
val composeTestRule = createComposeRule()
private val fakeBlockedByAdmin = FakeBlockedByAdmin()
+ private val fakeBlockedByEcm = FakeBlockedByEcm()
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
@@ -141,6 +143,29 @@ class RestrictedSwitchPreferenceTest {
assertThat(fakeBlockedByAdmin.sendShowAdminSupportDetailsIntentIsCalled).isTrue()
}
+ @Test
+ fun whenBlockedByEcm_disabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
+ composeTestRule.onNodeWithText(FakeBlockedByEcm.SUMMARY).assertIsDisplayed()
+ composeTestRule.onNode(isOn()).assertIsDisplayed()
+ }
+
+ @Test
+ fun whenBlockedByEcm_click() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(fakeBlockedByEcm.showRestrictedSettingsDetailsIsCalled).isTrue()
+ }
+
private fun setContent(restrictions: Restrictions) {
composeTestRule.setContent {
RestrictedSwitchPreference(switchPreferenceModel, restrictions) { _, _ ->
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt
index 2ccf323de2a3..556adc750763 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt
@@ -29,6 +29,7 @@ import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByAdmin
+import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByEcm
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -41,6 +42,7 @@ class RestrictedMenuItemTest {
val composeTestRule = createComposeRule()
private val fakeBlockedByAdmin = FakeBlockedByAdmin()
+ private val fakeBlockedByEcm = FakeBlockedByEcm()
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
@@ -129,6 +131,28 @@ class RestrictedMenuItemTest {
assertThat(menuItemOnClickIsCalled).isFalse()
}
+ @Test
+ fun whenBlockedByEcm_disabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TEXT).assertIsDisplayed().assertIsEnabled()
+ }
+
+ @Test
+ fun whenBlockedByEcm_onClick_showEcmDetails() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(fakeBlockedByEcm.showRestrictedSettingsDetailsIsCalled).isTrue()
+ assertThat(menuItemOnClickIsCalled).isFalse()
+ }
+
private fun setContent(restrictions: Restrictions) {
val fakeMoreOptionsScope = object : MoreOptionsScope() {
override fun dismiss() {}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt
index 93fa17d1eeca..f8ca2a084f14 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt
@@ -19,6 +19,7 @@ package com.android.settingslib.spaprivileged.tests.testutils
import androidx.compose.runtime.Composable
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
+import com.android.settingslib.spaprivileged.model.enterprise.BlockedByEcm
import com.android.settingslib.spaprivileged.model.enterprise.RestrictedMode
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProvider
@@ -36,6 +37,18 @@ class FakeBlockedByAdmin : BlockedByAdmin {
}
}
+class FakeBlockedByEcm : BlockedByEcm {
+ var showRestrictedSettingsDetailsIsCalled = false
+
+ override fun showRestrictedSettingsDetails() {
+ showRestrictedSettingsDetailsIsCalled = true
+ }
+
+ companion object {
+ const val SUMMARY = "Disabled"
+ }
+}
+
class FakeRestrictionsProvider : RestrictionsProvider {
var restrictedMode: RestrictedMode? = null
diff --git a/packages/SettingsLib/res/drawable/ic_bt_le_audio_sharing.xml b/packages/SettingsLib/res/drawable/ic_bt_le_audio_sharing.xml
new file mode 100644
index 000000000000..618677389ce1
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_le_audio_sharing.xml
@@ -0,0 +1,87 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#000000"
+ android:pathData="M16.984,24H7.279L12.131,15.508L16.984,24ZM10.481,22.144H13.781L12.131,19.257L10.481,22.144Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M12.131,14.295C13.471,14.295 14.558,13.209 14.558,11.869C14.558,10.529 13.471,9.442 12.131,9.442C10.791,9.442 9.705,10.529 9.705,11.869C9.705,13.209 10.791,14.295 12.131,14.295Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M4.573,21.368C4.052,20.943 3.967,20.179 4.379,19.657C4.804,19.136 5.568,19.051 6.09,19.463C6.611,19.876 6.696,20.64 6.284,21.174C6.041,21.465 5.689,21.623 5.338,21.623C5.071,21.623 4.804,21.538 4.573,21.368Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M17.991,21.162C17.579,20.628 17.663,19.876 18.185,19.451C18.707,19.039 19.471,19.124 19.896,19.646C20.308,20.167 20.223,20.931 19.702,21.344C19.471,21.526 19.204,21.611 18.949,21.611C18.586,21.611 18.234,21.453 17.991,21.162Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M1.213,17.145C0.91,16.551 1.165,15.823 1.771,15.532C2.378,15.241 3.093,15.495 3.397,16.09C3.688,16.697 3.433,17.424 2.827,17.715C2.657,17.8 2.475,17.837 2.305,17.837C1.844,17.837 1.419,17.582 1.213,17.145Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M21.449,17.691C20.842,17.4 20.588,16.684 20.879,16.077C21.17,15.471 21.898,15.216 22.504,15.507C23.099,15.798 23.354,16.526 23.062,17.133C22.856,17.557 22.419,17.812 21.971,17.812C21.789,17.812 21.619,17.776 21.449,17.691Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M0,11.892C0,11.225 0.546,10.679 1.213,10.679C1.88,10.679 2.426,11.212 2.426,11.892C2.426,12.559 1.88,13.105 1.213,13.105C0.546,13.105 0,12.559 0,11.892Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M21.837,11.869C21.837,11.857 21.837,11.845 21.837,11.833C21.824,11.153 22.37,10.62 23.05,10.607C23.717,10.607 24.251,11.153 24.263,11.821C24.263,11.833 24.263,11.845 24.263,11.845C24.263,11.857 24.263,11.869 24.263,11.869C24.263,12.536 23.717,13.082 23.05,13.082C22.382,13.082 21.837,12.536 21.837,11.869Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M1.759,8.242C1.152,7.963 0.898,7.235 1.189,6.628C1.48,6.022 2.196,5.767 2.802,6.058C3.409,6.349 3.664,7.077 3.372,7.684C3.166,8.108 2.729,8.363 2.281,8.363C2.099,8.363 1.929,8.327 1.759,8.242Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M20.866,7.622C20.563,7.028 20.818,6.3 21.424,6.009C22.019,5.706 22.747,5.96 23.038,6.567C23.038,6.567 23.038,6.567 23.05,6.567C23.341,7.161 23.087,7.889 22.48,8.181C22.31,8.265 22.128,8.302 21.958,8.302C21.509,8.302 21.073,8.059 20.866,7.622Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M4.355,4.104C3.931,3.582 4.016,2.818 4.537,2.406C5.071,1.981 5.823,2.066 6.248,2.588C6.672,3.109 6.588,3.874 6.066,4.298C5.835,4.48 5.569,4.565 5.302,4.565C4.95,4.565 4.598,4.407 4.355,4.104Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M18.161,4.262C17.627,3.838 17.542,3.073 17.955,2.552C18.379,2.03 19.132,1.945 19.666,2.358C20.187,2.77 20.272,3.534 19.86,4.068C19.617,4.359 19.265,4.517 18.913,4.517C18.646,4.517 18.379,4.432 18.161,4.262Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M8.492,1.497C8.334,0.854 8.747,0.199 9.402,0.041C10.057,-0.105 10.7,0.308 10.858,0.963C11.003,1.606 10.591,2.261 9.948,2.407C9.851,2.431 9.754,2.443 9.669,2.443C9.123,2.443 8.613,2.067 8.492,1.497Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M14.267,2.395C13.599,2.249 13.199,1.606 13.345,0.951C13.49,0.296 14.133,-0.116 14.788,0.029C15.443,0.175 15.856,0.83 15.71,1.485C15.589,2.043 15.08,2.431 14.534,2.431C14.437,2.431 14.352,2.419 14.267,2.395Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M7,17.037C6.527,16.564 6.527,15.8 7,15.326C7.473,14.841 8.237,14.841 8.71,15.314C9.196,15.787 9.196,16.552 8.723,17.025C8.48,17.267 8.177,17.389 7.861,17.389C7.546,17.389 7.242,17.267 7,17.037Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M15.565,17.012C15.092,16.539 15.092,15.762 15.565,15.289C16.038,14.816 16.814,14.816 17.288,15.289C17.761,15.762 17.761,16.539 17.288,17.012C17.045,17.243 16.742,17.364 16.426,17.364C16.111,17.364 15.807,17.243 15.565,17.012Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M4.853,11.917C4.853,11.237 5.386,10.691 6.054,10.691C6.721,10.691 7.279,11.225 7.279,11.892C7.279,12.56 6.745,13.106 6.078,13.118C5.398,13.118 4.853,12.584 4.853,11.917Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M16.984,11.868C16.984,11.856 16.984,11.844 16.984,11.832C16.984,11.832 16.984,11.82 16.984,11.807C16.972,11.14 17.506,10.582 18.185,10.582C18.852,10.57 19.398,11.116 19.41,11.783C19.41,11.795 19.41,11.82 19.41,11.832C19.41,11.844 19.41,11.856 19.41,11.868C19.41,12.535 18.865,13.081 18.197,13.081C17.53,13.081 16.984,12.535 16.984,11.868Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M6.952,8.471C6.478,7.997 6.478,7.233 6.952,6.76C6.952,6.76 6.952,6.76 6.939,6.76C7.413,6.275 8.189,6.275 8.662,6.748C9.135,7.221 9.147,7.985 8.674,8.458C8.432,8.701 8.116,8.822 7.813,8.822C7.497,8.822 7.194,8.701 6.952,8.471Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M15.529,8.399C15.043,7.938 15.043,7.161 15.504,6.688C15.977,6.203 16.742,6.203 17.227,6.664C17.7,7.137 17.712,7.901 17.239,8.387C17.009,8.629 16.693,8.751 16.378,8.751C16.075,8.751 15.759,8.629 15.529,8.399Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M10.87,5.815C10.858,5.148 11.392,4.59 12.071,4.59C12.738,4.578 13.284,5.124 13.284,5.791C13.296,6.458 12.762,7.016 12.083,7.016C11.416,7.016 10.87,6.483 10.87,5.815Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 3729b00b8baf..8d3582a736fe 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> oor tot vol"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> oor tot vol"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laaiproses word geoptimeer"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laaiproses word geoptimeer"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Laai"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laai tans vinnig"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 87916340f739..f6931ad30353 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"እስኪሞላ ድረስ <xliff:g id="TIME">%1$s</xliff:g> ይቀራል"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - እስኪሞላ ድረስ <xliff:g id="TIME">%2$s</xliff:g> ይቀራል"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ኃይል መሙላት እንዲተባ ተደርጓል"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ኃይል መሙላት እንዲተባ ተደርጓል"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ያልታወቀ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ኃይል በመሙላት ላይ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ኃይል በፍጥነት በመሙላት ላይ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index e0b56074dba2..92dc69ca54fe 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"يتبقّى <xliff:g id="TIME">%1$s</xliff:g> حتى اكتمال شحن البطارية."</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - يتبقّى <xliff:g id="TIME">%2$s</xliff:g> حتى اكتمال شحن البطارية."</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - تم تحسين الشحن"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - تم تحسين الشحن"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"غير معروف"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"جارٍ الشحن"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"جارٍ الشحن سريعًا"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 9a4268a84e38..48d3df4621f3 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"সম্পূৰ্ণ হ’বলৈ <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"সম্পূৰ্ণ হ’বলৈ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - চাৰ্জিং অপ্টিমাইজ কৰা হৈছে"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - চাৰ্জিং অপ্টিমাইজ কৰা হৈছে"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"অজ্ঞাত"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"চাৰ্জ কৰি থকা হৈছে"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্ৰুততাৰে চাৰ্জ হৈছে"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index a217406fb0f0..e27a40cdf7b1 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Tam şarj edilənədək <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - tam şarj edilənədək <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj optimallaşdırılıb"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj optimallaşdırılıb"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Naməlum"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Enerji doldurma"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Sürətlə doldurulur"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 76429ca3fe20..dd48b70bd8ed 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do kraja punjenja"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do kraja punjenja"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je optimizovano"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je optimizovano"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Puni se"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo se puni"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index e6bb3061b269..48247a01034f 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Да поўнай зарадкі засталося <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – да поўнай зарадкі засталося: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зарадка аптымізавана"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зарадка аптымізавана"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Невядома"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Зарадка"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хуткая зарадка"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 9f406ee2a8fd..035b1dd19c5a 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оставащо време до пълно зареждане: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зареждането е оптимизирано"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зареждането е оптимизирано"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Зарежда се"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Зарежда се бързо"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 1262c1efa804..9a2f88c1eb1f 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>-এ ব্যাটারি পুরো চার্জ হয়ে যাবে"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-এ ব্যাটারি পুরো চার্জ হয়ে যাবে"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - চার্জিং অপ্টিমাইজ করা হয়েছে"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - চার্জিং অপ্টিমাইজ করা হয়েছে"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"অজানা"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"চার্জ হচ্ছে"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্রুত চার্জ হচ্ছে"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index d806e6213c05..01662dd8bed6 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do potpune napunjenosti"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do potpune napunjenosti"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je optimizirano"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je optimizirano"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 3e7a5a1ebb1f..61898881baac 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g>: càrrega optimitzada"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g>: càrrega optimitzada"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconegut"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"S\'està carregant"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregant ràpidament"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 23b0c823104c..ec104b4a95e4 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do úplného nabití"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – optimalizované nabíjení"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – optimalizované nabíjení"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Neznámé"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíjí se"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rychlé nabíjení"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 972073fd6ada..b92fc040cd30 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Fuldt opladet om <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – fuldt opladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – opladning er optimeret"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – opladning optimeres"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ukendt"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Oplader"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Oplader hurtigt"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 52cdba17c924..debdb1e6b361 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Voll in <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laden wird optimiert"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laden wird optimiert"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unbekannt"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Wird aufgeladen"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Schnelles Aufladen"</string>
@@ -599,7 +600,7 @@
<string name="user_add_user_message_long" msgid="1527434966294733380">"Du kannst dieses Gerät zusammen mit anderen nutzen, indem du weitere Nutzer erstellst. Jeder erhält einen eigenen Bereich, in dem er Apps, den Hintergrund usw. personalisieren kann. Außerdem lassen sich Geräteeinstellungen wie WLAN ändern, die sich auf alle Nutzer auswirken.\n\nWenn du einen neuen Nutzer hinzufügst, muss dieser seinen Bereich einrichten.\n\nJeder Nutzer kann Apps für alle anderen Nutzer aktualisieren. Einstel­lun­gen und Dienste für die Bedie­nungs­hil­fen werden mög­licher­weise nicht auf den neuen Nutzer übertragen."</string>
<string name="user_add_user_message_short" msgid="3295959985795716166">"Wenn du einen neuen Nutzer hinzufügst, muss dieser seinen Bereich einrichten.\n\nJeder Nutzer kann Apps für alle anderen Nutzer aktualisieren."</string>
<string name="user_grant_admin_title" msgid="5157031020083343984">"Diesen Nutzer als Administrator festlegen?"</string>
- <string name="user_grant_admin_message" msgid="1673791931033486709">"Im Gegensatz zu anderen Nutzern haben Administratoren besondere Berechtigungen. Ein Administrator kann alle Nutzer verwalten, dieses Gerät aktualisieren oder zurücksetzen, Einstellungen ändern, alle installierten Apps sehen und anderen Administrator­berech­ti­gungen gewähren oder diese widerrufen."</string>
+ <string name="user_grant_admin_message" msgid="1673791931033486709">"Im Gegensatz zu anderen Nutzern haben Administratoren besondere Berechtigungen. Ein Administrator kann alle Nutzer verwalten, dieses Gerät aktualisieren oder zurücksetzen, Einstellungen ändern, alle installierten Apps sehen und anderen Administrator­­­berech­ti­gungen gewähren oder diese widerrufen."</string>
<string name="user_grant_admin_button" msgid="5441486731331725756">"Als Administrator festlegen"</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"Nutzer jetzt einrichten?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"Die Person muss Zugang zum Gerät haben und bereit sein, ihren Bereich einzurichten."</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 3c93c6218121..6cf05597feb2 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> για πλήρη φόρτιση"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Απομένουν <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Η φόρτιση βελτιστοποιήθηκε"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Η φόρτιση βελτιστοποιήθηκε"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Άγνωστο"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Φόρτιση"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ταχεία φόρτιση"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 199f30f1f8e4..b2c5718c999f 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 6a118876e75e..21fff5e1e3be 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -478,7 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimized"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimized"</string>
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 199f30f1f8e4..b2c5718c999f 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 199f30f1f8e4..b2c5718c999f 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index f4bdc120bc49..25e2779c4f5c 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -478,7 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ left until full‎‏‎‎‏‎"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - ‎‏‎‎‏‏‎<xliff:g id="TIME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ left until full‎‏‎‎‏‎"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‎‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Charging optimized‎‏‎‎‏‎"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Charging optimized‎‏‎‎‏‎"</string>
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‏‏‏‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Charging‎‏‎‎‏‎"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‎‎‎‏‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎Unknown‎‏‎‎‏‎"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎‎‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎Charging‎‏‎‎‏‎"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎Charging rapidly‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 6d2bd55d0612..d0ba34c4c8f4 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> para completar"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga optimizada"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga optimizada"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápidamente"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 0e689f88d921..77049e0c6837 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> hasta la carga completa"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hasta la carga completa"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga optimizada"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga optimizada"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carga rápida"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index e4d192575b8b..6a3c208efa59 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Täislaadimiseks kulub <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – täislaadimiseks kulub <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine on optimeeritud"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine on optimeeritud"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tundmatu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Laadimine"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiirlaadimine"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index d4346f7ab3dc..64e372ac182a 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> guztiz kargatu arte"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kargatzeko modu optimizatua"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kargatzeko modu optimizatua"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ezezaguna"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Kargatzen"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Bizkor kargatzen"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 68f3d3d74957..01d4618a319e 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> تا شارژ کامل باقی مانده است"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل باقی مانده است"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - شارژ بهینه شده است"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - شارژ بهینه شده است"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ناشناس"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"در حال شارژ شدن"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"درحال شارژ شدن سریع"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index dc23e14b6190..529bed536f1f 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> kunnes täynnä"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Lataus optimoitu"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Lataus optimoitu"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tuntematon"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Ladataan"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Nopea lataus"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 78f0ded467af..ea2383425dc9 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la recharge complète"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la recharge complète)"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge optimisée"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge optimisée"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charge en cours…"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Recharge rapide"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index d3bd3d1b3b42..dbf107acf46e 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Chargée à 100 %% dans <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge optimisée"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge optimisée"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Batterie en charge"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charge rapide"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index fef8f803b03b..233c9d4d6a1b 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> para completar a carga"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> para completar a carga)"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> (carga optimizada)"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> (carga optimizada)"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Descoñecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rapidamente"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index f664a9873c10..762ed28a4317 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી છે"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%2$s</xliff:g> બાકી છે"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ ઑપ્ટિમાઇઝ કરવામાં આવ્યું છે"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ ઑપ્ટિમાઇઝ કરવામાં આવ્યું છે"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"અજાણ્યું"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ચાર્જ થઈ રહ્યું છે"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ઝડપથી ચાર્જ થાય છે"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 31b14ce31247..6a0e3e0f706f 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> में बैटरी पूरी चार्ज हो जाएगी"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> में बैटरी पूरी चार्ज हो जाएगी"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग को ऑप्टिमाइज़ किया गया"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग को ऑप्टिमाइज़ किया गया"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हो रही है"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"तेज़ चार्ज हो रही है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 98e2f0197263..f019936ed9c4 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do napunjenosti"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje se optimizira"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje se optimizira"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 4f9452ad1716..d3d2960b22b9 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> a teljes töltöttségig"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes töltöttségig"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimalizált töltés"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimalizált töltés"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ismeretlen"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Töltés"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Gyorstöltés"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index ad4ca1b3ec72..4c14ae12fbb1 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Լիցքավորումն օպտիմալացված է"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Լիցքավորումն օպտիմալացված է"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Անհայտ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Լիցքավորում"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 449540a1a059..1379ecfc99b7 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> lagi sampai penuh"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi sampai penuh"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengisian daya dioptimalkan"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengisian daya dioptimalkan"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Mengisi daya"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengisi daya cepat"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index f1ce78b6dc35..e48d063d9d3c 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> fram að fullri hleðslu"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> fram að fullri hleðslu"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Hleðsla fínstillt"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Hleðsla fínstillt"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Óþekkt"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Í hleðslu"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hröð hleðsla"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 60455a169063..b33551440684 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> alla ricarica completa"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla ricarica completa"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ricarica ottimizzata"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ricarica ottimizzata"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Sconosciuta"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"In carica"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ricarica veloce"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 18fec960877c..a559cc616105 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"הזמן הנותר לטעינה מלאה: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – הזמן הנותר לטעינה מלאה: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – הטעינה עברה אופטימיזציה"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – הטעינה עברה אופטימיזציה"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"לא ידוע"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"בטעינה"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"הסוללה נטענת מהר"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 7a4eaf133b48..a7be818b3f85 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"完了まであと <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - 完了まであと <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電が最適化されています"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電が最適化されています"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"急速充電中"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 7c4d736be98f..eb06df31e676 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"სრულ დატენვამდე დარჩენილია <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> — სრულ დატენვამდე დარჩენილია <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - დატენვა ოპტიმიზირებულია"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - დატენვა ოპტიმიზირებულია"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"უცნობი"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"იტენება"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"სწრაფად იტენება"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index df0474fdedc0..86da0a4c0179 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Толық зарядталғанға дейін <xliff:g id="TIME">%1$s</xliff:g> қалды."</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: толық зарядталуға <xliff:g id="TIME">%2$s</xliff:g> қалды"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядтау оңтайландырылды"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядтау оңтайландырылды."</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Белгісіз"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Зарядталуда"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Жылдам зарядтау"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index ce1738ddfcce..848e9049df33 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើបពេញ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - នៅសល់ <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើបពេញ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - បានបង្កើនប្រសិទ្ធភាពនៃការសាក"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - បានបង្កើនប្រសិទ្ធភាពនៃការសាក"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"មិន​ស្គាល់"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"កំពុងសាក​ថ្ម"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 203cc6d04ea4..d13019c28fe5 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> - ಸಮಯದಲ್ಲಿ ಪೂರ್ತಿ ಚಾರ್ಜ್ ಆಗುತ್ತದೆ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ಸಮಯದಲ್ಲಿ ಪೂರ್ತಿ ಚಾರ್ಜ್ ಆಗುತ್ತದೆ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗಿದೆ"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ಅಪರಿಚಿತ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ವೇಗದ ಚಾರ್ಜಿಂಗ್"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index d52d71692f0b..3c8ad01f0f02 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> 후 충전 완료"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 최적화됨"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 최적화됨"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"알 수 없음"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"충전 중"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"고속 충전 중"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 20c476ad473a..cec61bbdf764 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> кийин толук кубатталат"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> кийин толук кубатталат"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> — Кубаттоо жакшыртылды"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> — Кубаттоо жакшыртылды"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Белгисиз"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Кубатталууда"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ыкчам кубатталууда"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index e0b7e8442478..420925920982 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ຍັງເຫຼືອອີກ <xliff:g id="TIME">%1$s</xliff:g> ຈຶ່ງຈະສາກເຕັມ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"ຍັງເຫຼືອອີກ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງຈະສາກເຕັມ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ການສາກຖືກປັບໃຫ້ເໝາະສົມແລ້ວ"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ການສາກຖືກປັບໃຫ້ເໝາະສົມແລ້ວ"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ບໍ່ຮູ້ຈັກ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ກຳລັງສາກໄຟ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ກຳລັງສາກໄຟດ່ວນ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index de2f1c34801c..cb0069d66e42 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Liko <xliff:g id="TIME">%1$s</xliff:g>, kol bus visiškai įkrauta"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – liko <xliff:g id="TIME">%2$s</xliff:g>, kol bus visiškai įkrauta"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – įkrovimas optimizuotas"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – įkrovimas optimizuotas"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nežinomas"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Kraunasi..."</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Greitai įkraunama"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 8f09595c86eb..db566881f016 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> līdz pilnai uzlādei"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> — uzlāde optimizēta"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> — uzlāde optimizēta"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nezināms"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Uzlāde"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Notiek ātrā uzlāde"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 0e03472d6703..6738e2068ae1 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до полна батерија"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полна батерија"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Полнењето е оптимизирано"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Полнењето е оптимизирано"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Се полни"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо полнење"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 5a1bcff7a8c0..a5c38bc22d1f 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"പൂർണ്ണമാകാൻ <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമാകാൻ <xliff:g id="TIME">%2$s</xliff:g> ശേഷിക്കുന്നു"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ചാർജിംഗ് ഒപ്റ്റിമൈസ് ചെയ്തു"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ചാർജിംഗ് ഒപ്റ്റിമൈസ് ചെയ്തു"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"അജ്ഞാതം"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ചാർജ് ചെയ്യുന്നു"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"അതിവേഗ ചാർജിംഗ്"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 81636be50a8e..c03c89f68b18 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Дүүрэх хүртэл <xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - дүүрэх хүртэл <xliff:g id="TIME">%2$s</xliff:g> үлдсэн"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэх явцыг оновчилсон"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэх явцыг оновчилсон"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Тодорхойгүй"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Цэнэглэж байна"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хурдан цэнэглэж байна"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index a85042dd2042..bbe782da8f85 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -52,7 +52,7 @@
<string name="wifi_remembered" msgid="3266709779723179188">"सेव्ह केले"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"डिस्कनेक्ट केले"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"अक्षम"</string>
- <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP कॉंफिगरेशन अयशस्वी"</string>
+ <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP कॉन्फिगरेशन अयशस्वी"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"प्रमाणीकरण समस्या"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"कनेक्ट करू शकत नाही"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\'शी कनेक्‍ट करू शकत नाही"</string>
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"पूर्ण चार्ज होण्यासाठी <xliff:g id="TIME">%1$s</xliff:g> शिल्लक आहेत"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूर्ण चार्ज होण्यासाठी <xliff:g id="TIME">%2$s</xliff:g> शिल्लक आहे"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग ऑप्टिमाइझ केले"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग ऑप्टिमाइझ केले"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज होत आहे"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"वेगाने चार्ज होत आहे"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 8292b6947b64..3da4f08be11b 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> lagi sebelum penuh"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi sebelum penuh"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengecasan dioptimumkan"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengecasan dioptimumkan"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Mengecas"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengecas dgn cepat"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 656cf599a37b..f4b669773aeb 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"အားပြည့်ရန် <xliff:g id="TIME">%1$s</xliff:g> လိုသည်"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားပြည့်ရန် <xliff:g id="TIME">%2$s</xliff:g> လိုသည်"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းခြင်းကို အကောင်းဆုံးပြင်ဆင်ထားသည်"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းခြင်းကို အကောင်းဆုံးပြင်ဆင်ထားသည်"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"မသိ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"အားသွင်းနေပါသည်"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"အမြန် အားသွင်းနေသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 6023c848ada8..fbdc9e33f576 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Fulladet om <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Fulladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladingen er optimalisert"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladingen er optimalisert"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ukjent"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Lader"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Lader raskt"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index e488df723478..245a31f9fcae 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"पूरा चार्ज हुन <xliff:g id="TIME">%1$s</xliff:g> लाग्ने छ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूरा चार्ज हुन <xliff:g id="TIME">%2$s</xliff:g> लाग्ने छ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज गर्ने प्रक्रिया अप्टिमाइज गरिएको छ"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज गर्ने प्रक्रिया अप्टिमाइज गरिएको छ"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हुँदै छ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै छ"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 92fb1052bb30..4eafb5740e05 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Vol over <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - vol over <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Opladen geoptimaliseerd"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Opladen geoptimaliseerd"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Opladen"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Snel opladen"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 621d8e10f828..5a8bed43faf7 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ପୂର୍ଣ୍ଣ ହେବାକୁ ଆଉ <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - ପୂର୍ଣ୍ଣ ହେବାକୁ ଆଉ <xliff:g id="TIME">%2$s</xliff:g> ବାକି ଅଛି"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ଚାର୍ଜିଂକୁ ଅପ୍ଟିମାଇଜ କରାଯାଇଛି"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ଚାର୍ଜିଂକୁ ଅପ୍ଟିମାଇଜ କରାଯାଇଛି"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ଅଜ୍ଞାତ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ଚାର୍ଜ ହେଉଛି"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index b32c07737ab8..df1fea17f645 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ਬੈਟਰੀ ਪੂਰੀ ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਪੂਰੀ ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%2$s</xliff:g> ਬਾਕੀ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜਿੰਗ ਨੂੰ ਸੁਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜਿੰਗ ਨੂੰ ਸੁਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ਅਗਿਆਤ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index aa84b69f0b6e..e9f9da8e095b 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do pełnego naładowania"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – ładowanie zoptymalizowane"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – ładowanie zoptymalizowane"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nieznane"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Ładowanie"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 49607a44c55a..4ee1cbd417ef 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até a conclusão"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a conclusão"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento otimizado"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento otimizado"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Carregando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 485c29601782..eae5cc965f7f 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até à carga máxima"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até à carga máxima"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g>: carregamento otimizado"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g>: carregamento otimizado"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"A carregar"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregamento rápido"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 49607a44c55a..4ee1cbd417ef 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até a conclusão"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a conclusão"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento otimizado"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento otimizado"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Carregando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 5fa52bbe08d6..91261d49f15e 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> până la finalizare"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> până la finalizare"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Încărcare optimizată"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Încărcare optimizată"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Necunoscut"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Se încarcă"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Se încarcă rapid"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 3a404882a57d..87b2a938fa31 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до полной зарядки"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядка оптимизирована"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядка оптимизирована"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Идет зарядка"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Быстрая зарядка"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index bf55a30c67f5..72bd507a5175 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"සම්පූර්ණ වීමට <xliff:g id="TIME">%1$s</xliff:g>ක් ඉතිරියි"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - සම්පූර්ණ වීමට <xliff:g id="TIME">%2$s</xliff:g>ක් ඉතිරියි"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය ප්‍රශස්ත කර ඇත"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය ප්‍රශස්ත කර ඇත"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"නොදනී"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ආරෝපණය වෙමින්"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ශීඝ්‍ර ආරෝපණය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 045a7243bf26..dd8be3a1d7a0 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do úplného nabitia"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Nabíjanie je optimalizované"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Nabíjanie je optimalizované"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Neznáme"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíja sa"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rýchle nabíjanie"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 1b13195fc7f6..eefb119eca02 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Še <xliff:g id="TIME">%1$s</xliff:g> do napolnjenosti"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – še <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – polnjenje je optimizirano"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – polnjenje je optimizirano"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Neznano"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Polnjenje"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hitro polnjenje"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index d14772b259cb..e847c515bb7e 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> derisa të mbushet"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të mbushet"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikimi u optimizua"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikimi u optimizua"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"I panjohur"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Po karikohet"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Karikim i shpejtë"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index fe8c11f58024..ed0e9a6ea975 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до краја пуњења"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до краја пуњења"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – пуњење је оптимизовано"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – пуњење је оптимизовано"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Пуни се"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо се пуни"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 7606b41e1d2f..b4de50408daa 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> kvar tills fulladdat"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kvar tills fulladdat"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Laddningen har optimerats"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Laddningen har optimerats"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Okänd"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Laddar"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 4c1816dd2c52..40725c92d6ea 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Zimesalia <xliff:g id="TIME">%1$s</xliff:g> ijae chaji"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> zimesalia ijae chaji"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Hali ya kuchaji imeboreshwa"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Hali ya kuchaji imeboreshwa"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Haijulikani"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Inachaji"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Inachaji kwa kasi"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 1248205442d5..2fe243aee324 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"முழுவதும் சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g> ஆகும்"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - முழுவதும் சார்ஜாக <xliff:g id="TIME">%2$s</xliff:g> ஆகும்"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - சார்ஜிங் மேம்படுத்தப்பட்டது"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - சார்ஜிங் மேம்படுத்தப்பட்டது"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"அறியப்படாத"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"சார்ஜ் ஆகிறது"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"வேகமாக சார்ஜாகிறது"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 440ed0fdb618..43f2b835bc44 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జింగ్ ఆప్టిమైజ్ చేయబడింది"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జింగ్ ఆప్టిమైజ్ చేయబడింది"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"తెలియదు"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ఛార్జ్ అవుతోంది"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"వేగవంతమైన ఛార్జింగ్"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 68a7db63622e..8395a5185363 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"อีก <xliff:g id="TIME">%1$s</xliff:g>จึงจะเต็ม"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - อีก <xliff:g id="TIME">%2$s</xliff:g> จึงจะเต็ม"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ปรับการชาร์จให้เหมาะสมแล้ว"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ปรับการชาร์จให้เหมาะสมแล้ว"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ไม่ทราบ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"กำลังชาร์จ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"กำลังชาร์จอย่างเร็ว"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index f5430ee852cf..a337bd8a69f2 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> na lang bago mapuno"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> na lang bago mapuno"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Naka-optimize ang pag-charge"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Naka-optimize ang pag-charge"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Hindi Kilala"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Nagcha-charge"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mabilis na charge"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 3cd35963ebea..43e18edca594 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Tamamen şarj olmasına <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj işlemi optimize edildi"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj işlemi optimize edildi"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hızlı şarj oluyor"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 080765c9edcb..984bb7978294 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до повного заряду"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджання оптимізовано"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджання оптимізовано"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Невідомо"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Заряджається"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index fd22bb69df49..bc8a46748d8b 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"مکمل چارج ہونے میں <xliff:g id="TIME">%1$s</xliff:g> باقی ہے"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"مکمل چارج ہونے میں <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> باقی ہے"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - چارجنگ کو بہتر بنایا گیا"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - چارجنگ کو بہتر بنایا گیا"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"نامعلوم"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"چارج ہو رہا ہے"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"تیزی سے چارج ہو رہا ہے"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index cdf5c282ad2b..a655dc0f0304 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Toʻlishiga <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Toʻlishiga <xliff:g id="TIME">%2$s</xliff:g> qoldi"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quvvatlash optimallashtirildi"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quvvatlash optimallashtirildi"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Noma’lum"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Quvvat olmoqda"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Tezkor quvvat olmoqda"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 16dbbabce8d3..643f8ca85499 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> nữa là pin đầy"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> nữa là pin đầy"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Quá trình sạc được tối ưu hoá"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Quá trình sạc được tối ưu hoá"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Không xác định"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Đang sạc"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Đang sạc nhanh"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index feb738dbca35..3b08e1f27bd6 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"还需<xliff:g id="TIME">%1$s</xliff:g>充满"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充电方式已优化"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充电方式已优化"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"正在充电"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充电"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 2b59ac8519be..eba782e54e48 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>後充滿電"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充滿電"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 已優化充電"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - 已優化充電"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 02a254fd55a9..099ad0364b0e 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>後充飽"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電效能已最佳化"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電效能將最佳化"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 6a7033ef7063..4d652f819ff3 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> okusele kuze kugcwale"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> okusele kuze kugcwale"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ukushaja kuthuthukisiwe"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ukushaja kuthuthukisiwe"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Akwaziwa"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Iyashaja"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ishaja ngokushesha"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
new file mode 100644
index 000000000000..6578eb7d50a6
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
@@ -0,0 +1,70 @@
+/*
+ * 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.settingslib;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.preference.DropDownPreference;
+import androidx.preference.PreferenceViewHolder;
+
+public class RestrictedDropDownPreference extends DropDownPreference {
+ RestrictedPreferenceHelper mHelper;
+
+ public RestrictedDropDownPreference(@NonNull Context context) {
+ super(context);
+ mHelper = new RestrictedPreferenceHelper(context, this, null);
+ }
+
+ /**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
+ */
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ mHelper.onBindViewHolder(holder);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (enabled && isDisabledByEcm()) {
+ mHelper.setDisabledByEcm(null);
+ return;
+ }
+
+ super.setEnabled(enabled);
+ }
+
+ @Override
+ public void performClick() {
+ if (!mHelper.performClick()) {
+ super.performClick();
+ }
+ }
+
+ public boolean isDisabledByEcm() {
+ return mHelper.isDisabledByEcm();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index d9024575f247..f36da19afd30 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -23,11 +23,11 @@ import static android.app.role.RoleManager.ROLE_FINANCED_DEVICE_KIOSK;
import static com.android.settingslib.Utils.getColorAttrDefaultColor;
-import android.Manifest;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
+import android.app.ecm.EnhancedConfirmationManager;
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
@@ -42,12 +42,10 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager.EnforcingUser;
-import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
-import android.util.ArraySet;
import android.util.Log;
import android.view.MenuItem;
import android.widget.TextView;
@@ -60,7 +58,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import java.util.List;
-import java.util.Set;
/**
* Utility class to host methods usable in adding a restricted padlock icon and showing admin
@@ -70,24 +67,11 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
private static final String LOG_TAG = "RestrictedLockUtils";
private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
- private static final Set<String> ECM_KEYS = new ArraySet<>();
// TODO(b/281701062): reference role name from role manager once its exposed.
private static final String ROLE_DEVICE_LOCK_CONTROLLER =
"android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER";
- static {
- if (android.security.Flags.extendEcmToAllSettings()) {
- ECM_KEYS.add(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW);
- ECM_KEYS.add(AppOpsManager.OPSTR_GET_USAGE_STATS);
- ECM_KEYS.add(AppOpsManager.OPSTR_LOADER_USAGE_STATS);
- ECM_KEYS.add(Manifest.permission.BIND_DEVICE_ADMIN);
- }
-
- ECM_KEYS.add(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS);
- ECM_KEYS.add(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
- }
-
/**
* @return drawables for displaying with settings that are locked by a device admin.
*/
@@ -112,32 +96,63 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
*/
@Nullable
public static Intent checkIfRequiresEnhancedConfirmation(@NonNull Context context,
- @NonNull String restriction,
- int uid,
- @Nullable String packageName) {
- // TODO(b/297372999): Replace with call to mainline module once ready
+ @NonNull String settingIdentifier, @NonNull String packageName) {
- if (!ECM_KEYS.contains(restriction)) {
+ if (!android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ || !android.security.Flags.extendEcmToAllSettings()) {
return null;
}
- final AppOpsManager appOps = (AppOpsManager) context
- .getSystemService(Context.APP_OPS_SERVICE);
- final int mode = appOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- uid, packageName, null, null);
- final boolean ecmEnabled = context.getResources().getBoolean(
- com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
- if (ecmEnabled && mode != AppOpsManager.MODE_ALLOWED) {
- final Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
- intent.putExtra(Intent.EXTRA_UID, uid);
- return intent;
+ EnhancedConfirmationManager ecManager = (EnhancedConfirmationManager) context
+ .getSystemService(Context.ECM_ENHANCED_CONFIRMATION_SERVICE);
+ try {
+ if (ecManager.isRestricted(packageName, settingIdentifier)) {
+ return ecManager.createRestrictedSettingDialogIntent(
+ packageName, settingIdentifier);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "package not found: " + packageName, e);
}
return null;
}
/**
+ * <p>This is {@code true} when the setting is a protected setting (i.e., a sensitive resource),
+ * and the app is restricted (i.e., considered dangerous), and the user has not yet cleared the
+ * app's restriction status (i.e., by clicking "Allow restricted settings" for this app). *
+ */
+ public static boolean isEnhancedConfirmationRestricted(@NonNull Context context,
+ @NonNull String settingIdentifier, @NonNull String packageName) {
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ try {
+ return context.getSystemService(EnhancedConfirmationManager.class)
+ .isRestricted(packageName, settingIdentifier);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Exception when retrieving package:" + packageName, e);
+ return false;
+ }
+ } else {
+ try {
+ if (!settingIdentifier.equals(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE)) {
+ return false;
+ }
+ int uid = context.getPackageManager().getPackageUid(packageName, 0);
+ final int mode = context.getSystemService(AppOpsManager.class)
+ .noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+ uid, packageName);
+ final boolean ecmEnabled = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
+ return ecmEnabled && mode != AppOpsManager.MODE_ALLOWED;
+ } catch (Exception e) {
+ // Fallback in case if app ops is not available in testing.
+ return false;
+ }
+ }
+ }
+
+ /**
* Checks if a restriction is enforced on a user and returns the enforced admin and
* admin userId.
*
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index 50e3bd08026c..495410b0a8ae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -23,6 +23,7 @@ import android.os.Process;
import android.os.UserHandle;
import android.util.AttributeSet;
+import androidx.annotation.NonNull;
import androidx.core.content.res.TypedArrayUtils;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceViewHolder;
@@ -99,12 +100,12 @@ public class RestrictedPreference extends TwoTargetPreference {
/**
* Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
* package. Marks the preference as disabled if so.
- * @param restriction The key identifying the setting
- * @param packageName the package to check the restriction for
- * @param uid the uid of the package
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
*/
- public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
- mHelper.checkEcmRestrictionAndSetDisabled(restriction, packageName, uid);
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index a479269f40fb..734b92c7ac6e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -31,6 +31,8 @@ import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
@@ -43,9 +45,17 @@ import com.android.settingslib.utils.BuildCompatUtils;
* by device admins via user restrictions.
*/
public class RestrictedPreferenceHelper {
+ private static final String TAG = "RestrictedPreferenceHelper";
+
private final Context mContext;
private final Preference mPreference;
String packageName;
+
+ /**
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
int uid;
private boolean mDisabledByAdmin;
@@ -148,14 +158,15 @@ public class RestrictedPreferenceHelper {
return true;
}
if (mDisabledByEcm) {
- if (android.security.Flags.extendEcmToAllSettings()) {
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
mContext.startActivity(mDisabledByEcmIntent);
return true;
+ } else {
+ RestrictedLockUtilsInternal.sendShowRestrictedSettingDialogIntent(mContext,
+ packageName, uid);
+ return true;
}
-
- RestrictedLockUtilsInternal.sendShowRestrictedSettingDialogIntent(mContext, packageName,
- uid);
- return true;
}
return false;
}
@@ -184,14 +195,14 @@ public class RestrictedPreferenceHelper {
/**
* Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
* package. Marks the preference as disabled if so.
- * @param restriction The key identifying the setting
- * @param packageName the package to check the restriction for
- * @param uid the uid of the package
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
*/
- public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
- updatePackageDetails(packageName, uid);
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ updatePackageDetails(packageName, android.os.Process.INVALID_UID);
Intent intent = RestrictedLockUtilsInternal.checkIfRequiresEnhancedConfirmation(
- mContext, restriction, uid, packageName);
+ mContext, settingIdentifier, packageName);
setDisabledByEcm(intent);
}
@@ -240,7 +251,7 @@ public class RestrictedPreferenceHelper {
* be disabled.
* @return true if the disabled state was changed.
*/
- public boolean setDisabledByEcm(Intent disabledIntent) {
+ public boolean setDisabledByEcm(@Nullable Intent disabledIntent) {
boolean disabled = disabledIntent != null;
boolean changed = false;
if (mDisabledByEcm != disabled) {
@@ -275,6 +286,10 @@ public class RestrictedPreferenceHelper {
if (mPreference instanceof PrimarySwitchPreference) {
((PrimarySwitchPreference) mPreference).setSwitchEnabled(isEnabled);
}
+
+ if (!isEnabled && mDisabledByEcm) {
+ mPreference.setSummary(R.string.disabled_by_app_ops_text);
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 70ece0fa8076..0c54c1903742 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -200,12 +200,12 @@ public class RestrictedSwitchPreference extends SwitchPreferenceCompat {
/**
* Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
* package. Marks the preference as disabled if so.
- * @param restriction The key identifying the setting
- * @param packageName the package to check the restriction for
- * @param uid the uid of the package
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
*/
- public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
- mHelper.checkEcmRestrictionAndSetDisabled(restriction, packageName, uid);
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index bcdb64d17636..61c3ce7f6988 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -300,37 +300,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
mLocalNapRoleConnected = false;
}
- if (!HearingAidStatsLogUtils.isUserCategorized(mContext)) {
- if (HearingAidStatsLogUtils.isJustBonded(getAddress())) {
- // Saves bonded timestamp as the source for judging whether to display
- // the survey
- if (getProfiles().stream().anyMatch(
- p -> (p instanceof HearingAidProfile
- || p instanceof HapClientProfile))) {
- HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext,
- HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_PAIRED);
- } else if (getProfiles().stream().anyMatch(
- p -> (p instanceof A2dpSinkProfile || p instanceof HeadsetProfile))) {
- HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext,
- HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_PAIRED);
- }
- HearingAidStatsLogUtils.removeFromJustBonded(getAddress());
- }
-
- // Saves connected timestamp as the source for judging whether to display
- // the survey
- if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
- if (profile instanceof HearingAidProfile
- || profile instanceof HapClientProfile) {
- HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext,
- HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_CONNECTED);
- } else if (profile instanceof A2dpSinkProfile
- || profile instanceof HeadsetProfile) {
- HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext,
- HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_CONNECTED);
- }
- }
- }
+ HearingAidStatsLogUtils.updateHistoryIfNeeded(mContext, this, profile, newProfileState);
}
fetchActiveDevices();
@@ -987,11 +957,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
connect();
}
- if (!HearingAidStatsLogUtils.isUserCategorized(mContext)) {
- // Saves this device as just bonded and checks if it's an hearing device after
- // profiles are connected. This is for judging whether to display the survey.
- HearingAidStatsLogUtils.addToJustBonded(getAddress());
- }
+ // Saves this device as just bonded and checks if it's an hearing device after
+ // profiles are connected. This is for judging whether to display the survey.
+ HearingAidStatsLogUtils.addToJustBonded(getAddress());
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index 9fd174d4586c..ca47efdc5df3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -337,51 +337,39 @@ public class HearingAidDeviceManager {
return null;
}
- private boolean isLeAudioHearingAid(CachedBluetoothDevice cachedDevice) {
- List<LocalBluetoothProfile> profiles = cachedDevice.getProfiles();
- boolean supportLeAudio = profiles.stream().anyMatch(p -> p instanceof LeAudioProfile);
- boolean supportHapClient = profiles.stream().anyMatch(p -> p instanceof HapClientProfile);
- return supportLeAudio && supportHapClient;
- }
-
- private boolean isAshaHearingAid(CachedBluetoothDevice cachedDevice) {
- return cachedDevice.getProfiles().stream().anyMatch(p -> p instanceof HearingAidProfile);
- }
-
private HearingAidInfo generateHearingAidInfo(CachedBluetoothDevice cachedDevice) {
final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
- if (isAshaHearingAid(cachedDevice)) {
- final HearingAidProfile asha = profileManager.getHearingAidProfile();
- if (asha == null) {
- Log.w(TAG, "HearingAidProfile is not supported on this device");
- } else {
- long hiSyncId = asha.getHiSyncId(cachedDevice.getDevice());
- if (isValidHiSyncId(hiSyncId)) {
- final HearingAidInfo.Builder infoBuilder = new HearingAidInfo.Builder()
- .setAshaDeviceSide(asha.getDeviceSide(cachedDevice.getDevice()))
- .setAshaDeviceMode(asha.getDeviceMode(cachedDevice.getDevice()))
- .setHiSyncId(hiSyncId);
- return infoBuilder.build();
- }
+
+ final HearingAidProfile asha = profileManager.getHearingAidProfile();
+ if (asha == null) {
+ Log.w(TAG, "HearingAidProfile is not supported on this device");
+ } else {
+ long hiSyncId = asha.getHiSyncId(cachedDevice.getDevice());
+ if (isValidHiSyncId(hiSyncId)) {
+ final HearingAidInfo.Builder infoBuilder = new HearingAidInfo.Builder()
+ .setAshaDeviceSide(asha.getDeviceSide(cachedDevice.getDevice()))
+ .setAshaDeviceMode(asha.getDeviceMode(cachedDevice.getDevice()))
+ .setHiSyncId(hiSyncId);
+ return infoBuilder.build();
}
}
- if (isLeAudioHearingAid(cachedDevice)) {
- final HapClientProfile hapClientProfile = profileManager.getHapClientProfile();
- final LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile();
- if (hapClientProfile == null || leAudioProfile == null) {
- Log.w(TAG, "HapClientProfile or LeAudioProfile is not supported on this device");
- } else {
- int audioLocation = leAudioProfile.getAudioLocation(cachedDevice.getDevice());
- int hearingAidType = hapClientProfile.getHearingAidType(cachedDevice.getDevice());
- if (audioLocation != BluetoothLeAudio.AUDIO_LOCATION_INVALID
- && hearingAidType != HapClientProfile.HearingAidType.TYPE_INVALID) {
- final HearingAidInfo.Builder infoBuilder = new HearingAidInfo.Builder()
- .setLeAudioLocation(audioLocation)
- .setHapDeviceType(hearingAidType);
- return infoBuilder.build();
- }
+
+ final HapClientProfile hapClientProfile = profileManager.getHapClientProfile();
+ final LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile();
+ if (hapClientProfile == null || leAudioProfile == null) {
+ Log.w(TAG, "HapClientProfile or LeAudioProfile is not supported on this device");
+ } else {
+ int audioLocation = leAudioProfile.getAudioLocation(cachedDevice.getDevice());
+ int hearingAidType = hapClientProfile.getHearingAidType(cachedDevice.getDevice());
+ if (audioLocation != BluetoothLeAudio.AUDIO_LOCATION_INVALID
+ && hearingAidType != HapClientProfile.HearingAidType.TYPE_INVALID) {
+ final HearingAidInfo.Builder infoBuilder = new HearingAidInfo.Builder()
+ .setLeAudioLocation(audioLocation)
+ .setHapDeviceType(hearingAidType);
+ return infoBuilder.build();
}
}
+
return null;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtils.java
index 97b94da60274..8e3df8bcc2dd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtils.java
@@ -16,10 +16,9 @@
package com.android.settingslib.bluetooth;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.SharedPreferences;
-import android.icu.text.SimpleDateFormat;
-import android.icu.util.TimeZone;
import android.util.Log;
import androidx.annotation.IntDef;
@@ -30,12 +29,14 @@ import com.android.internal.util.FrameworkStatsLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
-import java.util.Locale;
import java.util.Set;
-import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/** Utils class to report hearing aid metrics to statsd */
@@ -54,13 +55,13 @@ public final class HearingAidStatsLogUtils {
private static final String BT_HEARING_USER_CATEGORY = "bt_hearing_user_category";
private static final String HISTORY_RECORD_DELIMITER = ",";
- private static final String CATEGORY_HEARING_AIDS = "A11yHearingAidsUser";
- private static final String CATEGORY_NEW_HEARING_AIDS = "A11yNewHearingAidsUser";
- private static final String CATEGORY_HEARING_DEVICES = "A11yHearingDevicesUser";
- private static final String CATEGORY_NEW_HEARING_DEVICES = "A11yNewHearingDevicesUser";
+ static final String CATEGORY_HEARING_AIDS = "A11yHearingAidsUser";
+ static final String CATEGORY_NEW_HEARING_AIDS = "A11yNewHearingAidsUser";
+ static final String CATEGORY_HEARING_DEVICES = "A11yHearingDevicesUser";
+ static final String CATEGORY_NEW_HEARING_DEVICES = "A11yNewHearingDevicesUser";
- private static final long PAIRED_HISTORY_EXPIRED_TIME = TimeUnit.DAYS.toMillis(30);
- private static final long CONNECTED_HISTORY_EXPIRED_TIME = TimeUnit.DAYS.toMillis(7);
+ static final int PAIRED_HISTORY_EXPIRED_DAY = 30;
+ static final int CONNECTED_HISTORY_EXPIRED_DAY = 7;
private static final int VALID_PAIRED_EVENT_COUNT = 1;
private static final int VALID_CONNECTED_EVENT_COUNT = 7;
@@ -126,16 +127,43 @@ public final class HearingAidStatsLogUtils {
}
/**
- * Indicates if user is categorized as one of {@link #CATEGORY_HEARING_AIDS},
- * {@link #CATEGORY_NEW_HEARING_AIDS}, {@link #CATEGORY_HEARING_DEVICES}, and
- * {@link #CATEGORY_NEW_HEARING_DEVICES}.
+ * Updates corresponding history if we found the device is a hearing device after profile state
+ * changed.
*
* @param context the request context
- * @return true if user is already categorized as one of interested group
+ * @param cachedDevice the remote device
+ * @param profile the profile that has a state changed
+ * @param profileState the new profile state
*/
- public static boolean isUserCategorized(Context context) {
- String userCategory = getSharedPreferences(context).getString(BT_HEARING_USER_CATEGORY, "");
- return !userCategory.isEmpty();
+ public static void updateHistoryIfNeeded(Context context, CachedBluetoothDevice cachedDevice,
+ LocalBluetoothProfile profile, int profileState) {
+
+ if (isJustBonded(cachedDevice.getAddress())) {
+ // Saves bonded timestamp as the source for judging whether to display
+ // the survey
+ if (cachedDevice.getProfiles().stream().anyMatch(
+ p -> (p instanceof HearingAidProfile || p instanceof HapClientProfile))) {
+ HearingAidStatsLogUtils.addCurrentTimeToHistory(context,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_PAIRED);
+ } else if (cachedDevice.getProfiles().stream().anyMatch(
+ p -> (p instanceof A2dpSinkProfile || p instanceof HeadsetProfile))) {
+ HearingAidStatsLogUtils.addCurrentTimeToHistory(context,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_PAIRED);
+ }
+ removeFromJustBonded(cachedDevice.getAddress());
+ }
+
+ // Saves connected timestamp as the source for judging whether to display
+ // the survey
+ if (profileState == BluetoothProfile.STATE_CONNECTED) {
+ if (profile instanceof HearingAidProfile || profile instanceof HapClientProfile) {
+ HearingAidStatsLogUtils.addCurrentTimeToHistory(context,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_CONNECTED);
+ } else if (profile instanceof A2dpSinkProfile || profile instanceof HeadsetProfile) {
+ HearingAidStatsLogUtils.addCurrentTimeToHistory(context,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_CONNECTED);
+ }
+ }
}
/**
@@ -186,14 +214,6 @@ public final class HearingAidStatsLogUtils {
userCategory = CATEGORY_HEARING_DEVICES;
}
}
-
- if (!userCategory.isEmpty()) {
- // History become useless once user is categorized. Clear all history.
- SharedPreferences.Editor editor = getSharedPreferences(context).edit();
- editor.putString(BT_HEARING_USER_CATEGORY, userCategory).apply();
- clearHistory(context);
- sJustBondedDeviceAddressSet.clear();
- }
return userCategory;
}
@@ -211,7 +231,7 @@ public final class HearingAidStatsLogUtils {
* Removes the device address from the just bonded list.
* @param address the device address
*/
- public static void removeFromJustBonded(String address) {
+ private static void removeFromJustBonded(String address) {
sJustBondedDeviceAddressSet.remove(address);
}
@@ -220,24 +240,11 @@ public final class HearingAidStatsLogUtils {
* @param address the device address
* @return true if the device address is in the just bonded list
*/
- public static boolean isJustBonded(String address) {
+ private static boolean isJustBonded(String address) {
return sJustBondedDeviceAddressSet.contains(address);
}
/**
- * Clears all BT hearing devices related history stored in shared preference.
- * @param context the request context
- */
- private static synchronized void clearHistory(Context context) {
- SharedPreferences.Editor editor = getSharedPreferences(context).edit();
- editor.remove(BT_HEARING_AIDS_PAIRED_HISTORY)
- .remove(BT_HEARING_AIDS_CONNECTED_HISTORY)
- .remove(BT_HEARING_DEVICES_PAIRED_HISTORY)
- .remove(BT_HEARING_DEVICES_CONNECTED_HISTORY)
- .apply();
- }
-
- /**
* Adds current timestamp into BT hearing devices related history.
* @param context the request context
* @param type the type of history to store the data. See {@link HistoryType}.
@@ -256,7 +263,7 @@ public final class HearingAidStatsLogUtils {
}
return;
}
- if (history.peekLast() != null && isSameDay(history.peekLast(), timestamp)) {
+ if (history.peekLast() != null && isSameDay(timestamp, history.peekLast())) {
if (DEBUG) {
Log.w(TAG, "Skip this record, it's same day record");
}
@@ -275,25 +282,25 @@ public final class HearingAidStatsLogUtils {
|| BT_HEARING_DEVICES_PAIRED_HISTORY.equals(spName)) {
LinkedList<Long> history = convertToHistoryList(
getSharedPreferences(context).getString(spName, ""));
- removeRecordsBeforeTime(history, PAIRED_HISTORY_EXPIRED_TIME);
+ removeRecordsBeforeDay(history, PAIRED_HISTORY_EXPIRED_DAY);
return history;
} else if (BT_HEARING_AIDS_CONNECTED_HISTORY.equals(spName)
|| BT_HEARING_DEVICES_CONNECTED_HISTORY.equals(spName)) {
LinkedList<Long> history = convertToHistoryList(
getSharedPreferences(context).getString(spName, ""));
- removeRecordsBeforeTime(history, CONNECTED_HISTORY_EXPIRED_TIME);
+ removeRecordsBeforeDay(history, CONNECTED_HISTORY_EXPIRED_DAY);
return history;
}
return null;
}
- private static void removeRecordsBeforeTime(LinkedList<Long> history, long time) {
- if (history == null) {
+ private static void removeRecordsBeforeDay(LinkedList<Long> history, int day) {
+ if (history == null || history.isEmpty()) {
return;
}
- Long currentTime = System.currentTimeMillis();
+ long currentTime = System.currentTimeMillis();
while (history.peekFirst() != null
- && currentTime - history.peekFirst() > time) {
+ && dayDifference(currentTime, history.peekFirst()) >= day) {
history.poll();
}
}
@@ -324,11 +331,13 @@ public final class HearingAidStatsLogUtils {
* @return {@code true} if two timestamps are on the same day
*/
private static boolean isSameDay(long t1, long t2) {
- final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd", Locale.getDefault());
- sdf.setTimeZone(TimeZone.getDefault());
- String dateString1 = sdf.format(t1);
- String dateString2 = sdf.format(t2);
- return dateString1.equals(dateString2);
+ return dayDifference(t1, t2) == 0;
+ }
+ private static long dayDifference(long t1, long t2) {
+ ZoneId zoneId = ZoneId.systemDefault();
+ LocalDate date1 = Instant.ofEpochMilli(t1).atZone(zoneId).toLocalDate();
+ LocalDate date2 = Instant.ofEpochMilli(t2).atZone(zoneId).toLocalDate();
+ return Math.abs(ChronoUnit.DAYS.between(date1, date2));
}
private static SharedPreferences getSharedPreferences(Context context) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index a376c1f77567..50e2f9cb13f7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -161,11 +161,6 @@ public abstract class InfoMediaManager extends MediaManager {
protected abstract void startScanOnRouter();
- /**
- * Transfer MediaDevice for media without package name.
- */
- protected abstract boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device);
-
protected abstract void transferToRoute(@NonNull MediaRoute2Info route);
protected abstract void selectRoute(
@@ -199,6 +194,12 @@ public abstract class InfoMediaManager extends MediaManager {
@NonNull
protected abstract List<RoutingSessionInfo> getRemoteSessions();
+ /**
+ * Returns a non-empty list containing the routing sessions associated to the target media app.
+ *
+ * <p> The first item of the list is always the {@link RoutingSessionInfo#isSystemSession()
+ * system session}, followed other remote sessions linked to the target media app.
+ */
@NonNull
protected abstract List<RoutingSessionInfo> getRoutingSessionsForPackage();
@@ -206,9 +207,6 @@ public abstract class InfoMediaManager extends MediaManager {
protected abstract RoutingSessionInfo getRoutingSessionById(@NonNull String sessionId);
@NonNull
- protected abstract List<MediaRoute2Info> getAllRoutes();
-
- @NonNull
protected abstract List<MediaRoute2Info> getAvailableRoutesFromRouter();
@NonNull
@@ -256,8 +254,8 @@ public abstract class InfoMediaManager extends MediaManager {
* @return If add device successful return {@code true}, otherwise return {@code false}
*/
boolean addDeviceToPlayMedia(MediaDevice device) {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null || !info.getSelectableRoutes().contains(device.mRouteInfo.getId())) {
+ final RoutingSessionInfo info = getActiveRoutingSession();
+ if (!info.getSelectableRoutes().contains(device.mRouteInfo.getId())) {
Log.w(TAG, "addDeviceToPlayMedia() Ignoring selecting a non-selectable device : "
+ device.getName());
return false;
@@ -267,13 +265,11 @@ public abstract class InfoMediaManager extends MediaManager {
return true;
}
- private RoutingSessionInfo getRoutingSessionInfo() {
- final List<RoutingSessionInfo> sessionInfos = getRoutingSessionsForPackage();
-
- if (sessionInfos.isEmpty()) {
- return null;
- }
- return sessionInfos.get(sessionInfos.size() - 1);
+ @NonNull
+ private RoutingSessionInfo getActiveRoutingSession() {
+ // List is never empty.
+ final List<RoutingSessionInfo> sessions = getRoutingSessionsForPackage();
+ return sessions.get(sessions.size() - 1);
}
boolean isRoutingSessionAvailableForVolumeControl() {
@@ -311,8 +307,8 @@ public abstract class InfoMediaManager extends MediaManager {
* @return If device stop successful return {@code true}, otherwise return {@code false}
*/
boolean removeDeviceFromPlayMedia(MediaDevice device) {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null || !info.getSelectedRoutes().contains(device.mRouteInfo.getId())) {
+ final RoutingSessionInfo info = getActiveRoutingSession();
+ if (!info.getSelectedRoutes().contains(device.mRouteInfo.getId())) {
Log.w(TAG, "removeDeviceFromMedia() Ignoring deselecting a non-deselectable device : "
+ device.getName());
return false;
@@ -326,13 +322,7 @@ public abstract class InfoMediaManager extends MediaManager {
* Release session to stop playing media on MediaDevice.
*/
boolean releaseSession() {
- final RoutingSessionInfo sessionInfo = getRoutingSessionInfo();
- if (sessionInfo == null) {
- Log.w(TAG, "releaseSession() Ignoring release session : " + mPackageName);
- return false;
- }
-
- releaseSession(sessionInfo);
+ releaseSession(getActiveRoutingSession());
return true;
}
@@ -342,12 +332,7 @@ public abstract class InfoMediaManager extends MediaManager {
*/
@NonNull
List<MediaDevice> getSelectableMediaDevices() {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "getSelectableMediaDevices() cannot find selectable MediaDevice from : "
- + mPackageName);
- return Collections.emptyList();
- }
+ final RoutingSessionInfo info = getActiveRoutingSession();
final List<MediaDevice> deviceList = new ArrayList<>();
for (MediaRoute2Info route : getSelectableRoutes(info)) {
@@ -364,12 +349,7 @@ public abstract class InfoMediaManager extends MediaManager {
*/
@NonNull
List<MediaDevice> getDeselectableMediaDevices() {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.d(TAG, "getDeselectableMediaDevices() cannot find deselectable MediaDevice from : "
- + mPackageName);
- return Collections.emptyList();
- }
+ final RoutingSessionInfo info = getActiveRoutingSession();
final List<MediaDevice> deviceList = new ArrayList<>();
for (MediaRoute2Info route : getDeselectableRoutes(info)) {
@@ -387,13 +367,7 @@ public abstract class InfoMediaManager extends MediaManager {
*/
@NonNull
List<MediaDevice> getSelectedMediaDevices() {
- RoutingSessionInfo info = getRoutingSessionInfo();
-
- if (info == null) {
- Log.w(TAG, "getSelectedMediaDevices() cannot find selectable MediaDevice from : "
- + mPackageName);
- return Collections.emptyList();
- }
+ RoutingSessionInfo info = getActiveRoutingSession();
final List<MediaDevice> deviceList = new ArrayList<>();
for (MediaRoute2Info route : getSelectedRoutes(info)) {
@@ -427,15 +401,8 @@ public abstract class InfoMediaManager extends MediaManager {
* @param volume the value of volume
*/
void adjustSessionVolume(int volume) {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "adjustSessionVolume() can't found corresponding RoutingSession with : "
- + mPackageName);
- return;
- }
-
Log.d(TAG, "adjustSessionVolume() adjust volume: " + volume + ", with : " + mPackageName);
- setSessionVolume(info, volume);
+ setSessionVolume(getActiveRoutingSession(), volume);
}
/**
@@ -444,14 +411,7 @@ public abstract class InfoMediaManager extends MediaManager {
* @return maximum volume of the session, and return -1 if not found.
*/
public int getSessionVolumeMax() {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "getSessionVolumeMax() can't find corresponding RoutingSession with : "
- + mPackageName);
- return -1;
- }
-
- return info.getVolumeMax();
+ return getActiveRoutingSession().getVolumeMax();
}
/**
@@ -460,24 +420,11 @@ public abstract class InfoMediaManager extends MediaManager {
* @return current volume of the session, and return -1 if not found.
*/
public int getSessionVolume() {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "getSessionVolume() can't find corresponding RoutingSession with : "
- + mPackageName);
- return -1;
- }
-
- return info.getVolume();
+ return getActiveRoutingSession().getVolume();
}
CharSequence getSessionName() {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "Unable to get session name for package: " + mPackageName);
- return null;
- }
-
- return info.getName();
+ return getActiveRoutingSession().getName();
}
@TargetApi(Build.VERSION_CODES.R)
@@ -503,26 +450,24 @@ public abstract class InfoMediaManager extends MediaManager {
}
}
private synchronized List<MediaRoute2Info> getAvailableRoutes() {
- List<MediaRoute2Info> infos = new ArrayList<>();
- RoutingSessionInfo routingSessionInfo = getRoutingSessionInfo();
- List<MediaRoute2Info> selectedRouteInfos = new ArrayList<>();
- if (routingSessionInfo != null) {
- selectedRouteInfos = getSelectedRoutes(routingSessionInfo);
- infos.addAll(selectedRouteInfos);
- infos.addAll(getSelectableRoutes(routingSessionInfo));
- }
- final List<MediaRoute2Info> transferableRoutes =
- getTransferableRoutes(mPackageName);
+ List<MediaRoute2Info> availableRoutes = new ArrayList<>();
+ RoutingSessionInfo activeSession = getActiveRoutingSession();
+
+ List<MediaRoute2Info> selectedRoutes = getSelectedRoutes(activeSession);
+ availableRoutes.addAll(selectedRoutes);
+ availableRoutes.addAll(getSelectableRoutes(activeSession));
+
+ final List<MediaRoute2Info> transferableRoutes = getTransferableRoutes(mPackageName);
for (MediaRoute2Info transferableRoute : transferableRoutes) {
boolean alreadyAdded = false;
- for (MediaRoute2Info mediaRoute2Info : infos) {
+ for (MediaRoute2Info mediaRoute2Info : availableRoutes) {
if (TextUtils.equals(transferableRoute.getId(), mediaRoute2Info.getId())) {
alreadyAdded = true;
break;
}
}
if (!alreadyAdded) {
- infos.add(transferableRoute);
+ availableRoutes.add(transferableRoute);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
@@ -531,13 +476,13 @@ public abstract class InfoMediaManager extends MediaManager {
final List<RouteListingPreference.Item> preferenceRouteListing =
Api34Impl.composePreferenceRouteListing(
routeListingPreference);
- infos = Api34Impl.arrangeRouteListByPreference(selectedRouteInfos,
+ availableRoutes = Api34Impl.arrangeRouteListByPreference(selectedRoutes,
getAvailableRoutesFromRouter(),
preferenceRouteListing);
}
- return Api34Impl.filterDuplicatedIds(infos);
+ return Api34Impl.filterDuplicatedIds(availableRoutes);
} else {
- return infos;
+ return availableRoutes;
}
}
@@ -610,7 +555,7 @@ public abstract class InfoMediaManager extends MediaManager {
}
if (mediaDevice != null
- && getRoutingSessionInfo().getSelectedRoutes().contains(route.getId())) {
+ && getActiveRoutingSession().getSelectedRoutes().contains(route.getId())) {
mediaDevice.setState(STATE_SELECTED);
if (mCurrentConnectedDevice == null) {
mCurrentConnectedDevice = mediaDevice;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index cf8906d282d0..453e807947cf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -86,18 +86,6 @@ public class ManagerInfoMediaManager extends InfoMediaManager {
}
@Override
- protected boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device) {
- final RoutingSessionInfo info = mRouterManager.getSystemRoutingSession(null);
- if (info != null) {
- // TODO: b/279555229 - provide real user handle and package name of a caller.
- mRouterManager.transfer(
- info, device.mRouteInfo, android.os.Process.myUserHandle(), mPackageName);
- return true;
- }
- return false;
- }
-
- @Override
protected void selectRoute(@NonNull MediaRoute2Info route, @NonNull RoutingSessionInfo info) {
mRouterManager.selectRoute(info, route);
}
@@ -174,12 +162,6 @@ public class ManagerInfoMediaManager extends InfoMediaManager {
@Override
@NonNull
- protected List<MediaRoute2Info> getAllRoutes() {
- return mRouterManager.getAllRoutes();
- }
-
- @Override
- @NonNull
protected List<MediaRoute2Info> getAvailableRoutesFromRouter() {
return mRouterManager.getAvailableRoutes(mPackageName);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
index 5b1c8efd8360..ea4de392e139 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
@@ -58,11 +58,6 @@ import java.util.List;
}
@Override
- protected boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device) {
- return false;
- }
-
- @Override
protected void transferToRoute(@NonNull MediaRoute2Info route) {
// Do nothing.
}
@@ -136,12 +131,6 @@ import java.util.List;
@NonNull
@Override
- protected List<MediaRoute2Info> getAllRoutes() {
- return Collections.emptyList();
- }
-
- @NonNull
- @Override
protected List<MediaRoute2Info> getAvailableRoutesFromRouter() {
return Collections.emptyList();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/OWNERS b/packages/SettingsLib/src/com/android/settingslib/media/OWNERS
index 3cae39f4ea40..7467ee1c1a7c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/media/OWNERS
@@ -1,4 +1,7 @@
# Default reviewers for this and subdirectories.
+ethibodeau@google.com
+michaelmikhil@google.com
+apotapov@google.com
shaoweishen@google.com
#Android Media - For minor changes and renames only.
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
index c8c8b672d976..0f08605a50d0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
@@ -114,17 +114,6 @@ public final class RouterInfoMediaManager extends InfoMediaManager {
}
@Override
- protected boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device) {
- if (device.mRouteInfo == null) {
- return false;
- }
-
- RoutingController controller = mRouter.getSystemController();
- mRouter.transfer(controller, device.mRouteInfo);
- return true;
- }
-
- @Override
protected void transferToRoute(@NonNull MediaRoute2Info route) {
mRouter.transferTo(route);
}
@@ -241,12 +230,6 @@ public final class RouterInfoMediaManager extends InfoMediaManager {
@NonNull
@Override
- protected List<MediaRoute2Info> getAllRoutes() {
- return mRouter.getAllRoutes();
- }
-
- @NonNull
- @Override
protected List<MediaRoute2Info> getAvailableRoutesFromRouter() {
return mRouter.getRoutes();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt
index 60983070b1cf..2a44511599f1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt
@@ -40,3 +40,21 @@ class FakeNotificationsSoundPolicyRepository : NotificationsSoundPolicyRepositor
mutableZenMode.value = zenMode
}
}
+
+fun FakeNotificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories: Int = 0,
+ priorityCallSenders: Int = NotificationManager.Policy.PRIORITY_SENDERS_ANY,
+ priorityMessageSenders: Int = NotificationManager.Policy.CONVERSATION_SENDERS_NONE,
+ suppressedVisualEffects: Int = NotificationManager.Policy.SUPPRESSED_EFFECTS_UNSET,
+ state: Int = NotificationManager.Policy.STATE_UNSET,
+ priorityConversationSenders: Int = NotificationManager.Policy.CONVERSATION_SENDERS_NONE,
+) = updateNotificationPolicy(
+ NotificationManager.Policy(
+ priorityCategories,
+ priorityCallSenders,
+ priorityMessageSenders,
+ suppressedVisualEffects,
+ state,
+ priorityConversationSenders,
+ )
+) \ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt
new file mode 100644
index 000000000000..794cf832f48b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.statusbar.notification.domain.interactor
+
+import android.app.NotificationManager
+import android.media.AudioManager
+import android.provider.Settings
+import android.service.notification.ZenModeConfig
+import com.android.settingslib.statusbar.notification.data.model.ZenMode
+import com.android.settingslib.statusbar.notification.data.repository.NotificationsSoundPolicyRepository
+import com.android.settingslib.volume.shared.model.AudioStream
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+/** Determines notification sounds state and limitations. */
+class NotificationsSoundPolicyInteractor(
+ private val repository: NotificationsSoundPolicyRepository
+) {
+
+ /** @see NotificationManager.getNotificationPolicy */
+ val notificationPolicy: StateFlow<NotificationManager.Policy?>
+ get() = repository.notificationPolicy
+
+ /** @see NotificationManager.getZenMode */
+ val zenMode: StateFlow<ZenMode?>
+ get() = repository.zenMode
+
+ /** Checks if [notificationPolicy] allows alarms. */
+ val areAlarmsAllowed: Flow<Boolean?> = notificationPolicy.map { it?.allowAlarms() }
+
+ /** Checks if [notificationPolicy] allows media. */
+ val isMediaAllowed: Flow<Boolean?> = notificationPolicy.map { it?.allowMedia() }
+
+ /** Checks if [notificationPolicy] allows ringer. */
+ val isRingerAllowed: Flow<Boolean?> =
+ notificationPolicy.map { policy ->
+ policy ?: return@map null
+ !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(policy)
+ }
+
+ /** Checks if the [stream] is muted by either [zenMode] or [notificationPolicy]. */
+ fun isZenMuted(stream: AudioStream): Flow<Boolean> {
+ return combine(
+ zenMode.filterNotNull(),
+ areAlarmsAllowed.filterNotNull(),
+ isMediaAllowed.filterNotNull(),
+ isRingerAllowed.filterNotNull(),
+ ) { zenMode, areAlarmsAllowed, isMediaAllowed, isRingerAllowed ->
+ if (zenMode.zenMode == Settings.Global.ZEN_MODE_NO_INTERRUPTIONS) {
+ return@combine true
+ }
+
+ val isNotificationOrRing =
+ stream.value == AudioManager.STREAM_RING ||
+ stream.value == AudioManager.STREAM_NOTIFICATION
+ if (isNotificationOrRing && zenMode.zenMode == Settings.Global.ZEN_MODE_ALARMS) {
+ return@combine true
+ }
+ if (zenMode.zenMode != Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
+ return@combine false
+ }
+
+ if (stream.value == AudioManager.STREAM_ALARM && !areAlarmsAllowed) {
+ return@combine true
+ }
+ if (stream.value == AudioManager.STREAM_MUSIC && !isMediaAllowed) {
+ return@combine true
+ }
+ if (isNotificationOrRing && !isRingerAllowed) {
+ return@combine true
+ }
+
+ return@combine false
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/OWNERS b/packages/SettingsLib/src/com/android/settingslib/volume/OWNERS
new file mode 100644
index 000000000000..75c7642fbed1
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/OWNERS
@@ -0,0 +1,5 @@
+apotapov@google.com
+ethibodeau@google.com
+michaelmikhil@google.com
+
+juliacr@google.com #{LAST_RESORT_SUGGESTION} \ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
index f729c04fb849..0df4615c8b7c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -21,10 +21,12 @@ import android.media.AudioManager
import android.media.AudioManager.OnCommunicationDeviceChangedListener
import androidx.concurrent.futures.DirectExecutor
import com.android.internal.util.ConcurrentUtils
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
+import com.android.settingslib.volume.shared.model.StreamAudioManagerEvent
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
@@ -33,9 +35,11 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -61,10 +65,10 @@ interface AudioRepository {
val communicationDevice: StateFlow<AudioDeviceInfo?>
/** State of the [AudioStream]. */
- suspend fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel>
+ fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel>
- /** Current state of the [AudioStream]. */
- suspend fun getCurrentAudioStream(audioStream: AudioStream): AudioStreamModel
+ /** Returns the last audible volume before stream was muted. */
+ suspend fun getLastAudibleVolume(audioStream: AudioStream): Int
suspend fun setVolume(audioStream: AudioStream, volume: Int)
@@ -72,7 +76,7 @@ interface AudioRepository {
}
class AudioRepositoryImpl(
- private val audioManagerIntentsReceiver: AudioManagerIntentsReceiver,
+ private val audioManagerEventsReceiver: AudioManagerEventsReceiver,
private val audioManager: AudioManager,
private val backgroundCoroutineContext: CoroutineContext,
private val coroutineScope: CoroutineScope,
@@ -89,8 +93,8 @@ class AudioRepositoryImpl(
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), audioManager.mode)
override val ringerMode: StateFlow<RingerMode> =
- audioManagerIntentsReceiver.intents
- .filter { AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION == it.action }
+ audioManagerEventsReceiver.events
+ .filterIsInstance(AudioManagerEvent.InternalRingerModeChanged::class)
.map { RingerMode(audioManager.ringerModeInternal) }
.flowOn(backgroundCoroutineContext)
.stateIn(
@@ -119,23 +123,34 @@ class AudioRepositoryImpl(
audioManager.communicationDevice,
)
- override suspend fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> {
- return audioManagerIntentsReceiver.intents
+ override fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> {
+ return audioManagerEventsReceiver.events
+ .filter {
+ if (it is StreamAudioManagerEvent) {
+ it.audioStream == audioStream
+ } else {
+ true
+ }
+ }
.map { getCurrentAudioStream(audioStream) }
+ .onStart { emit(getCurrentAudioStream(audioStream)) }
.flowOn(backgroundCoroutineContext)
}
- override suspend fun getCurrentAudioStream(audioStream: AudioStream): AudioStreamModel {
+ private fun getCurrentAudioStream(audioStream: AudioStream): AudioStreamModel {
+ return AudioStreamModel(
+ audioStream = audioStream,
+ minVolume = getMinVolume(audioStream),
+ maxVolume = audioManager.getStreamMaxVolume(audioStream.value),
+ volume = audioManager.getStreamVolume(audioStream.value),
+ isAffectedByRingerMode = audioManager.isStreamAffectedByRingerMode(audioStream.value),
+ isMuted = audioManager.isStreamMute(audioStream.value),
+ )
+ }
+
+ override suspend fun getLastAudibleVolume(audioStream: AudioStream): Int {
return withContext(backgroundCoroutineContext) {
- AudioStreamModel(
- audioStream = audioStream,
- minVolume = getMinVolume(audioStream),
- maxVolume = audioManager.getStreamMaxVolume(audioStream.value),
- volume = audioManager.getStreamVolume(audioStream.value),
- isAffectedByRingerMode =
- audioManager.isStreamAffectedByRingerMode(audioStream.value),
- isMuted = audioManager.isStreamMute(audioStream.value)
- )
+ audioManager.getLastAudibleStreamVolume(audioStream.value)
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
index aa9ae76c66c4..298dd71e555e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
@@ -15,13 +15,13 @@
*/
package com.android.settingslib.volume.data.repository
-import android.media.AudioManager
import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.model.RoutingSession
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
@@ -29,7 +29,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
@@ -54,7 +54,7 @@ interface LocalMediaRepository {
}
class LocalMediaRepositoryImpl(
- audioManagerIntentsReceiver: AudioManagerIntentsReceiver,
+ audioManagerEventsReceiver: AudioManagerEventsReceiver,
private val localMediaManager: LocalMediaManager,
private val mediaRouter2Manager: MediaRouter2Manager,
coroutineScope: CoroutineScope,
@@ -62,9 +62,9 @@ class LocalMediaRepositoryImpl(
) : LocalMediaRepository {
private val devicesChanges =
- audioManagerIntentsReceiver.intents.filter {
- AudioManager.STREAM_DEVICES_CHANGED_ACTION == it.action
- }
+ audioManagerEventsReceiver.events.filterIsInstance(
+ AudioManagerEvent.StreamDevicesChanged::class
+ )
private val mediaDevicesUpdates: Flow<DevicesUpdate> =
callbackFlow {
val callback =
@@ -109,6 +109,7 @@ class LocalMediaRepositoryImpl(
override val currentConnectedDevice: StateFlow<MediaDevice?> =
merge(devicesChanges, mediaDevicesUpdates)
.map { localMediaManager.currentConnectedDevice }
+ .onStart { emit(localMediaManager.currentConnectedDevice) }
.stateIn(
coroutineScope,
SharingStarted.WhileSubscribed(),
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt
index 6925c71fc68f..7c231d1fad4e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt
@@ -16,21 +16,19 @@
package com.android.settingslib.volume.data.repository
-import android.content.Intent
-import android.media.AudioManager
import android.media.session.MediaController
import android.media.session.MediaSessionManager
-import android.media.session.PlaybackState
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.bluetooth.headsetAudioModeChanges
import com.android.settingslib.media.session.activeMediaChanges
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onStart
@@ -44,7 +42,7 @@ interface MediaControllerRepository {
}
class MediaControllerRepositoryImpl(
- audioManagerIntentsReceiver: AudioManagerIntentsReceiver,
+ audioManagerEventsReceiver: AudioManagerEventsReceiver,
private val mediaSessionManager: MediaSessionManager,
localBluetoothManager: LocalBluetoothManager?,
coroutineScope: CoroutineScope,
@@ -52,9 +50,9 @@ class MediaControllerRepositoryImpl(
) : MediaControllerRepository {
private val devicesChanges =
- audioManagerIntentsReceiver.intents.filter {
- AudioManager.STREAM_DEVICES_CHANGED_ACTION == it.action
- }
+ audioManagerEventsReceiver.events.filterIsInstance(
+ AudioManagerEvent.StreamDevicesChanged::class
+ )
override val activeLocalMediaController: StateFlow<MediaController?> =
combine(
@@ -63,7 +61,7 @@ class MediaControllerRepositoryImpl(
},
localBluetoothManager?.headsetAudioModeChanges?.onStart { emit(Unit) }
?: flowOf(null),
- devicesChanges.onStart { emit(Intent()) },
+ devicesChanges.onStart { emit(AudioManagerEvent.StreamDevicesChanged) },
) { controllers, _, _ ->
controllers?.let(::findLocalMediaController)
}
@@ -98,9 +96,4 @@ class MediaControllerRepositoryImpl(
}
return localController
}
-
- private companion object {
- val inactivePlaybackStates =
- setOf(PlaybackState.STATE_STOPPED, PlaybackState.STATE_NONE, PlaybackState.STATE_ERROR)
- }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
new file mode 100644
index 000000000000..56b0bf74574f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.volume.domain.interactor
+
+import android.media.AudioManager
+import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor
+import com.android.settingslib.volume.data.repository.AudioRepository
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.settingslib.volume.shared.model.AudioStreamModel
+import com.android.settingslib.volume.shared.model.RingerMode
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/** Provides audio stream state and an ability to change it */
+class AudioVolumeInteractor(
+ private val audioRepository: AudioRepository,
+ private val notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor,
+) {
+
+ /** State of the [AudioStream]. */
+ fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> =
+ combine(
+ audioRepository.getAudioStream(audioStream),
+ audioRepository.ringerMode,
+ notificationsSoundPolicyInteractor.isZenMuted(audioStream)
+ ) { streamModel: AudioStreamModel, ringerMode: RingerMode, isZenMuted: Boolean ->
+ streamModel.copy(volume = processVolume(streamModel, ringerMode, isZenMuted))
+ }
+
+ suspend fun setVolume(audioStream: AudioStream, volume: Int) =
+ audioRepository.setVolume(audioStream, volume)
+
+ suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean) =
+ audioRepository.setMuted(audioStream, isMuted)
+
+ /** Checks if the volume can be changed via the UI. */
+ fun canChangeVolume(audioStream: AudioStream): Flow<Boolean> {
+ return if (audioStream.value == AudioManager.STREAM_NOTIFICATION) {
+ getAudioStream(AudioStream(AudioManager.STREAM_RING)).map { !it.isMuted }
+ } else {
+ flowOf(true)
+ }
+ }
+
+ private suspend fun processVolume(
+ audioStreamModel: AudioStreamModel,
+ ringerMode: RingerMode,
+ isZenMuted: Boolean,
+ ): Int {
+ if (isZenMuted) {
+ return audioRepository.getLastAudibleVolume(audioStreamModel.audioStream)
+ }
+ val isNotificationOrRing =
+ audioStreamModel.audioStream.value == AudioManager.STREAM_RING ||
+ audioStreamModel.audioStream.value == AudioManager.STREAM_NOTIFICATION
+ if (isNotificationOrRing && ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
+ // For ringer-mode affected streams, show volume as zero when ringer mode is vibrate
+ if (
+ audioStreamModel.audioStream.value == AudioManager.STREAM_RING ||
+ (audioStreamModel.audioStream.value == AudioManager.STREAM_NOTIFICATION &&
+ audioStreamModel.isMuted)
+ ) {
+ return 0
+ }
+ } else if (audioStreamModel.isMuted) {
+ return 0
+ }
+ return audioStreamModel.volume
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerIntentsReceiver.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiver.kt
index 9fa4c86cdea1..c3b1a7cb16e3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerIntentsReceiver.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiver.kt
@@ -21,6 +21,9 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.media.AudioManager
+import android.util.Log
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
+import com.android.settingslib.volume.shared.model.AudioStream
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharedFlow
@@ -28,19 +31,20 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch
-/** Exposes [AudioManager] intents as a observable shared flow. */
-interface AudioManagerIntentsReceiver {
+/** Exposes [AudioManager] events as a observable shared flow. */
+interface AudioManagerEventsReceiver {
- val intents: SharedFlow<Intent>
+ val events: SharedFlow<AudioManagerEvent>
}
-class AudioManagerIntentsReceiverImpl(
+class AudioManagerEventsReceiverImpl(
private val context: Context,
coroutineScope: CoroutineScope,
-) : AudioManagerIntentsReceiver {
+) : AudioManagerEventsReceiver {
private val allActions: Collection<String>
get() =
@@ -50,9 +54,10 @@ class AudioManagerIntentsReceiverImpl(
AudioManager.VOLUME_CHANGED_ACTION,
AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION,
AudioManager.STREAM_DEVICES_CHANGED_ACTION,
+ AudioManager.ACTION_VOLUME_CHANGED,
)
- override val intents: SharedFlow<Intent> =
+ override val events: SharedFlow<AudioManagerEvent> =
callbackFlow {
val receiver =
object : BroadcastReceiver() {
@@ -73,5 +78,34 @@ class AudioManagerIntentsReceiverImpl(
}
.filterNotNull()
.filter { intent -> allActions.contains(intent.action) }
+ .mapNotNull { it.toAudioManagerEvent() }
.shareIn(coroutineScope, SharingStarted.WhileSubscribed())
+
+ private fun Intent.toAudioManagerEvent(): AudioManagerEvent? {
+ when (action) {
+ AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION ->
+ return AudioManagerEvent.InternalRingerModeChanged
+ AudioManager.STREAM_DEVICES_CHANGED_ACTION ->
+ return AudioManagerEvent.StreamDevicesChanged
+ AudioManager.MASTER_MUTE_CHANGED_ACTION ->
+ return AudioManagerEvent.StreamMasterMuteChanged
+ }
+
+ val audioStreamType: Int =
+ getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.ERROR)
+ if (audioStreamType == AudioManager.ERROR) {
+ Log.e(
+ "AudioManagerIntentsReceiver",
+ "Intent doesn't have AudioManager.EXTRA_VOLUME_STREAM_TYPE extra",
+ )
+ return null
+ }
+ val audioStream = AudioStream(audioStreamType)
+ return when (action) {
+ AudioManager.STREAM_MUTE_CHANGED_ACTION ->
+ AudioManagerEvent.StreamMuteChanged(audioStream)
+ AudioManager.VOLUME_CHANGED_ACTION -> AudioManagerEvent.StreamVolumeChanged(audioStream)
+ else -> null
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioManagerEvent.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioManagerEvent.kt
new file mode 100644
index 000000000000..e19896bc5e87
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioManagerEvent.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.volume.shared.model
+
+/** Model events happening with the [android.media.AudioManager]. */
+sealed interface AudioManagerEvent {
+
+ data class StreamMuteChanged(override val audioStream: AudioStream) : StreamAudioManagerEvent
+
+ data class StreamVolumeChanged(override val audioStream: AudioStream) : StreamAudioManagerEvent
+
+ data object StreamMasterMuteChanged : AudioManagerEvent
+
+ data object InternalRingerModeChanged : AudioManagerEvent
+
+ data object StreamDevicesChanged : AudioManagerEvent
+}
+
+/** [AudioManagerEvent] that happens for a specific [AudioStream]. */
+sealed interface StreamAudioManagerEvent : AudioManagerEvent {
+
+ val audioStream: AudioStream
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioStream.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioStream.kt
index 58f3c2d61f3b..9c48299d81be 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioStream.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioStream.kt
@@ -25,7 +25,7 @@ value class AudioStream(val value: Int) {
require(value in supportedStreamTypes) { "Unsupported stream=$value" }
}
- private companion object {
+ companion object {
val supportedStreamTypes =
setOf(
AudioManager.STREAM_VOICE_CALL,
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/OWNERS b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/OWNERS
new file mode 100644
index 000000000000..b7ade19dbb1e
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/OWNERS
@@ -0,0 +1 @@
+include /packages/SettingsLib/src/com/android/settingslib/volume/OWNERS \ No newline at end of file
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
index 48b04db5b50b..1728a8022ce5 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
@@ -20,7 +20,8 @@ import android.media.AudioDeviceInfo
import android.media.AudioManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.settingslib.volume.shared.FakeAudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
@@ -46,7 +47,6 @@ import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@Suppress("UnspecifiedRegisterReceiverFlag")
@RunWith(AndroidJUnit4::class)
class AudioRepositoryTest {
@@ -59,7 +59,7 @@ class AudioRepositoryTest {
@Mock private lateinit var audioManager: AudioManager
@Mock private lateinit var communicationDevice: AudioDeviceInfo
- private val intentsReceiver = FakeAudioManagerIntentsReceiver()
+ private val eventsReceiver = FakeAudioManagerEventsReceiver()
private val volumeByStream: MutableMap<Int, Int> = mutableMapOf()
private val isAffectedByRingerModeByStream: MutableMap<Int, Boolean> = mutableMapOf()
private val isMuteByStream: MutableMap<Int, Boolean> = mutableMapOf()
@@ -77,12 +77,14 @@ class AudioRepositoryTest {
`when`(audioManager.getStreamMaxVolume(anyInt())).thenReturn(MAX_VOLUME)
`when`(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
`when`(audioManager.setStreamVolume(anyInt(), anyInt(), anyInt())).then {
- volumeByStream[it.arguments[0] as Int] = it.arguments[1] as Int
- triggerIntent(AudioManager.ACTION_VOLUME_CHANGED)
+ val streamType = it.arguments[1] as Int
+ volumeByStream[it.arguments[0] as Int] = streamType
+ triggerEvent(AudioManagerEvent.StreamVolumeChanged(AudioStream(streamType)))
}
`when`(audioManager.adjustStreamVolume(anyInt(), anyInt(), anyInt())).then {
- isMuteByStream[it.arguments[0] as Int] = it.arguments[2] == AudioManager.ADJUST_MUTE
- triggerIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION)
+ val streamType = it.arguments[0] as Int
+ isMuteByStream[streamType] = it.arguments[2] == AudioManager.ADJUST_MUTE
+ triggerEvent(AudioManagerEvent.StreamMuteChanged(AudioStream(streamType)))
}
`when`(audioManager.getStreamVolume(anyInt())).thenAnswer {
volumeByStream.getOrDefault(it.arguments[0] as Int, 0)
@@ -96,7 +98,7 @@ class AudioRepositoryTest {
underTest =
AudioRepositoryImpl(
- intentsReceiver,
+ eventsReceiver,
audioManager,
testScope.testScheduler,
testScope.backgroundScope,
@@ -125,7 +127,7 @@ class AudioRepositoryTest {
runCurrent()
`when`(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
- triggerIntent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)
+ triggerEvent(AudioManagerEvent.InternalRingerModeChanged)
runCurrent()
assertThat(modes)
@@ -179,24 +181,6 @@ class AudioRepositoryTest {
}
@Test
- fun adjustingVolume_currentModeIsUpToDate() {
- testScope.runTest {
- val audioStream = AudioStream(AudioManager.STREAM_SYSTEM)
- var streamModel: AudioStreamModel? = null
- underTest
- .getAudioStream(audioStream)
- .onEach { streamModel = it }
- .launchIn(backgroundScope)
- runCurrent()
-
- underTest.setVolume(audioStream, 50)
- runCurrent()
-
- assertThat(underTest.getCurrentAudioStream(audioStream)).isEqualTo(streamModel)
- }
- }
-
- @Test
fun muteStream_mutesTheStream() {
testScope.runTest {
val audioStream = AudioStream(AudioManager.STREAM_SYSTEM)
@@ -267,8 +251,8 @@ class AudioRepositoryTest {
modeListenerCaptor.value.onModeChanged(mode)
}
- private fun triggerIntent(action: String) {
- testScope.launch { intentsReceiver.triggerIntent(action) }
+ private fun triggerEvent(event: AudioManagerEvent) {
+ testScope.launch { eventsReceiver.triggerEvent(event) }
}
private companion object {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt
index dc9ea10a1074..2d12dae36ff1 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt
@@ -23,7 +23,7 @@ import androidx.test.filters.SmallTest
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.model.RoutingSession
-import com.android.settingslib.volume.shared.FakeAudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -58,7 +58,7 @@ class LocalMediaRepositoryImplTest {
@Captor
private lateinit var deviceCallbackCaptor: ArgumentCaptor<LocalMediaManager.DeviceCallback>
- private val intentsReceiver = FakeAudioManagerIntentsReceiver()
+ private val eventsReceiver = FakeAudioManagerEventsReceiver()
private val testScope = TestScope()
private lateinit var underTest: LocalMediaRepository
@@ -69,7 +69,7 @@ class LocalMediaRepositoryImplTest {
underTest =
LocalMediaRepositoryImpl(
- intentsReceiver,
+ eventsReceiver,
localMediaManager,
mediaRouter2Manager,
testScope.backgroundScope,
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt
index 7bd43d2cf8ab..f3d17141334e 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt
@@ -16,7 +16,6 @@
package com.android.settingslib.volume.data.repository
-import android.media.AudioManager
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSessionManager
@@ -26,7 +25,8 @@ import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.BluetoothCallback
import com.android.settingslib.bluetooth.BluetoothEventManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
-import com.android.settingslib.volume.shared.FakeAudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -66,7 +66,7 @@ class MediaControllerRepositoryImplTest {
@Mock private lateinit var localPlaybackInfo: PlaybackInfo
private val testScope = TestScope()
- private val intentsReceiver = FakeAudioManagerIntentsReceiver()
+ private val eventsReceiver = FakeAudioManagerEventsReceiver()
private lateinit var underTest: MediaControllerRepository
@@ -94,7 +94,7 @@ class MediaControllerRepositoryImplTest {
underTest =
MediaControllerRepositoryImpl(
- intentsReceiver,
+ eventsReceiver,
mediaSessionManager,
localBluetoothManager,
testScope.backgroundScope,
@@ -121,7 +121,7 @@ class MediaControllerRepositoryImplTest {
.launchIn(backgroundScope)
runCurrent()
- intentsReceiver.triggerIntent(AudioManager.STREAM_DEVICES_CHANGED_ACTION)
+ eventsReceiver.triggerEvent(AudioManagerEvent.StreamDevicesChanged)
triggerOnAudioModeChanged()
runCurrent()
@@ -146,7 +146,7 @@ class MediaControllerRepositoryImplTest {
.launchIn(backgroundScope)
runCurrent()
- intentsReceiver.triggerIntent(AudioManager.STREAM_DEVICES_CHANGED_ACTION)
+ eventsReceiver.triggerEvent(AudioManagerEvent.StreamDevicesChanged)
triggerOnAudioModeChanged()
runCurrent()
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt
new file mode 100644
index 000000000000..35ee8287d52f
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.volume.shared
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.media.AudioManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.google.common.truth.Expect
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+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.any
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@Suppress("UnspecifiedRegisterReceiverFlag")
+@RunWith(AndroidJUnit4::class)
+class AudioManagerEventsReceiverTest {
+
+ @JvmField @Rule val expect = Expect.create()
+ private val testScope = TestScope()
+
+ @Mock private lateinit var context: Context
+ @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver>
+
+ private lateinit var underTest: AudioManagerEventsReceiver
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest = AudioManagerEventsReceiverImpl(context, testScope.backgroundScope)
+ }
+
+ @Test
+ fun validIntent_translatedToEvent() {
+ testScope.runTest {
+ val events = mutableListOf<AudioManagerEvent>()
+ underTest.events.onEach { events.add(it) }.launchIn(backgroundScope)
+ runCurrent()
+
+ triggerIntent(
+ Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION).apply {
+ putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_SYSTEM)
+ }
+ )
+ triggerIntent(
+ Intent(AudioManager.VOLUME_CHANGED_ACTION).apply {
+ putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_SYSTEM)
+ }
+ )
+ triggerIntent(Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION))
+ triggerIntent(Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION))
+ triggerIntent(Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION))
+ runCurrent()
+
+ expect
+ .that(events)
+ .containsExactly(
+ AudioManagerEvent.StreamMuteChanged(
+ AudioStream(AudioManager.STREAM_SYSTEM),
+ ),
+ AudioManagerEvent.StreamVolumeChanged(
+ AudioStream(AudioManager.STREAM_SYSTEM),
+ ),
+ AudioManagerEvent.StreamMasterMuteChanged,
+ AudioManagerEvent.InternalRingerModeChanged,
+ AudioManagerEvent.StreamDevicesChanged,
+ )
+ }
+ }
+
+ @Test
+ fun streamAudioManagerEvent_withoutAudioStream_areSkipped() {
+ testScope.runTest {
+ val events = mutableListOf<AudioManagerEvent>()
+ underTest.events.onEach { events.add(it) }.launchIn(backgroundScope)
+ runCurrent()
+
+ triggerIntent(Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION))
+ triggerIntent(Intent(AudioManager.VOLUME_CHANGED_ACTION))
+ runCurrent()
+
+ expect.that(events).isEmpty()
+ }
+ }
+
+ @Test
+ fun invalidIntents_areSkipped() {
+ testScope.runTest {
+ val events = mutableListOf<AudioManagerEvent>()
+ underTest.events.onEach { events.add(it) }.launchIn(backgroundScope)
+ runCurrent()
+
+ triggerIntent(null)
+ triggerIntent(Intent())
+ triggerIntent(Intent("invalid_action"))
+ runCurrent()
+
+ expect.that(events).isEmpty()
+ }
+ }
+
+ private fun triggerIntent(intent: Intent?) {
+ verify(context).registerReceiver(receiverCaptor.capture(), any())
+ receiverCaptor.value.onReceive(context, intent)
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerIntentsReceiver.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerEventsReceiver.kt
index 530690a5faa9..b742df7afc46 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerIntentsReceiver.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerEventsReceiver.kt
@@ -16,21 +16,17 @@
package com.android.settingslib.volume.shared
-import android.content.Intent
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
-class FakeAudioManagerIntentsReceiver : AudioManagerIntentsReceiver {
+class FakeAudioManagerEventsReceiver : AudioManagerEventsReceiver {
- private val mutableIntents = MutableSharedFlow<Intent>()
- override val intents: SharedFlow<Intent> = mutableIntents.asSharedFlow()
+ private val mutableIntents = MutableSharedFlow<AudioManagerEvent>()
+ override val events: SharedFlow<AudioManagerEvent> = mutableIntents.asSharedFlow()
- suspend fun triggerIntent(intent: Intent) {
- mutableIntents.emit(intent)
- }
-
- suspend fun triggerIntent(action: String) {
- triggerIntent(Intent(action))
+ suspend fun triggerEvent(event: AudioManagerEvent) {
+ mutableIntents.emit(event)
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
index 701f00865da9..7ad54e187ae5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
@@ -17,6 +17,7 @@
package com.android.settingslib;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doReturn;
@@ -28,6 +29,7 @@ import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyResourcesManager;
import android.content.Context;
+import android.content.Intent;
import android.view.View;
import android.widget.TextView;
@@ -87,6 +89,19 @@ public class RestrictedPreferenceHelperTest {
}
@Test
+ public void bindPreference_disabledByEcm_shouldDisplayDisabledSummary() {
+ final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS);
+ when(mViewHolder.itemView.findViewById(android.R.id.summary))
+ .thenReturn(summaryView);
+
+ mHelper.setDisabledByEcm(mock(Intent.class));
+ mHelper.onBindViewHolder(mViewHolder);
+
+ verify(mPreference).setSummary(R.string.disabled_by_app_ops_text);
+ verify(summaryView, never()).setVisibility(View.GONE);
+ }
+
+ @Test
public void bindPreference_notDisabled_shouldNotHideSummary() {
final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS);
when(mViewHolder.itemView.findViewById(android.R.id.summary))
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtilsTest.java
index 8a75bdc7bc35..bd5a022e44e5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtilsTest.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static com.android.settingslib.bluetooth.HearingAidStatsLogUtils.CONNECTED_HISTORY_EXPIRED_DAY;
+import static com.android.settingslib.bluetooth.HearingAidStatsLogUtils.PAIRED_HISTORY_EXPIRED_DAY;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
@@ -34,6 +37,9 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
@@ -84,8 +90,8 @@ public class HearingAidStatsLogUtilsTest {
@Test
public void addCurrentTimeToHistory_addNewData() {
- final long currentTime = System.currentTimeMillis();
- final long lastData = currentTime - TimeUnit.DAYS.toMillis(2);
+ final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
+ final long lastData = todayStartOfDay - TimeUnit.DAYS.toMillis(6);
HearingAidStatsLogUtils.addToHistory(mContext, TEST_HISTORY_TYPE, lastData);
HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext, TEST_HISTORY_TYPE);
@@ -96,22 +102,21 @@ public class HearingAidStatsLogUtilsTest {
}
@Test
public void addCurrentTimeToHistory_skipSameDateData() {
- final long currentTime = System.currentTimeMillis();
- final long lastData = currentTime - 1;
- HearingAidStatsLogUtils.addToHistory(mContext, TEST_HISTORY_TYPE, lastData);
+ final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
+ HearingAidStatsLogUtils.addToHistory(mContext, TEST_HISTORY_TYPE, todayStartOfDay);
HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext, TEST_HISTORY_TYPE);
LinkedList<Long> history = HearingAidStatsLogUtils.getHistory(mContext, TEST_HISTORY_TYPE);
assertThat(history).isNotNull();
assertThat(history.size()).isEqualTo(1);
- assertThat(history.getFirst()).isEqualTo(lastData);
+ assertThat(history.getFirst()).isEqualTo(todayStartOfDay);
}
@Test
public void addCurrentTimeToHistory_cleanUpExpiredData() {
- final long currentTime = System.currentTimeMillis();
- final long expiredData = currentTime - TimeUnit.DAYS.toMillis(10);
+ final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
+ final long expiredData = todayStartOfDay - TimeUnit.DAYS.toMillis(6) - 1;
HearingAidStatsLogUtils.addToHistory(mContext, TEST_HISTORY_TYPE, expiredData);
HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext, TEST_HISTORY_TYPE);
@@ -121,4 +126,71 @@ public class HearingAidStatsLogUtilsTest {
assertThat(history.size()).isEqualTo(1);
assertThat(history.getFirst()).isNotEqualTo(expiredData);
}
+
+ @Test
+ public void getUserCategory_hearingAidsUser() {
+ prepareHearingAidsUserHistory();
+
+ assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
+ HearingAidStatsLogUtils.CATEGORY_HEARING_AIDS);
+ }
+
+ @Test
+ public void getUserCategory_newHearingAidsUser() {
+ prepareHearingAidsUserHistory();
+ prepareNewUserHistory();
+
+ assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
+ HearingAidStatsLogUtils.CATEGORY_NEW_HEARING_AIDS);
+ }
+
+ @Test
+ public void getUserCategory_hearingDevicesUser() {
+ prepareHearingDevicesUserHistory();
+
+ assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
+ HearingAidStatsLogUtils.CATEGORY_HEARING_DEVICES);
+ }
+
+ @Test
+ public void getUserCategory_newHearingDevicesUser() {
+ prepareHearingDevicesUserHistory();
+ prepareNewUserHistory();
+
+ assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
+ HearingAidStatsLogUtils.CATEGORY_NEW_HEARING_DEVICES);
+ }
+
+ private long convertToStartOfDayTime(long timestamp) {
+ ZoneId zoneId = ZoneId.systemDefault();
+ LocalDate date = Instant.ofEpochMilli(timestamp).atZone(zoneId).toLocalDate();
+ return date.atStartOfDay(zoneId).toInstant().toEpochMilli();
+ }
+
+ private void prepareHearingAidsUserHistory() {
+ final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
+ for (int i = CONNECTED_HISTORY_EXPIRED_DAY - 1; i >= 0; i--) {
+ final long data = todayStartOfDay - TimeUnit.DAYS.toMillis(i);
+ HearingAidStatsLogUtils.addToHistory(mContext,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_CONNECTED, data);
+ }
+ }
+
+ private void prepareHearingDevicesUserHistory() {
+ final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
+ for (int i = CONNECTED_HISTORY_EXPIRED_DAY - 1; i >= 0; i--) {
+ final long data = todayStartOfDay - TimeUnit.DAYS.toMillis(i);
+ HearingAidStatsLogUtils.addToHistory(mContext,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_CONNECTED, data);
+ }
+ }
+
+ private void prepareNewUserHistory() {
+ final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
+ final long data = todayStartOfDay - TimeUnit.DAYS.toMillis(PAIRED_HISTORY_EXPIRED_DAY - 1);
+ HearingAidStatsLogUtils.addToHistory(mContext,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_PAIRED, data);
+ HearingAidStatsLogUtils.addToHistory(mContext,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_PAIRED, data);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 290e63cace51..c159d5ee37f0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -30,6 +30,7 @@ import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.S
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -124,7 +125,11 @@ public class InfoMediaManagerTest {
@Test
public void stopScan_startFirst_callsUnregister() {
+ RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
mInfoMediaManager.mRouterManager = mRouterManager;
+ // Since test is running in Robolectric, return a fake session to avoid NPE.
+ when(mRouterManager.getRoutingSessions(anyString())).thenReturn(List.of(sessionInfo));
+
mInfoMediaManager.startScan();
mInfoMediaManager.stopScan();
@@ -500,18 +505,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void connectDeviceWithoutPackageName_noSession_returnFalse() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- final MediaDevice device = new InfoMediaDevice(mContext, info);
-
- final List<RoutingSessionInfo> infos = new ArrayList<>();
-
- mShadowRouter2Manager.setRemoteSessions(infos);
-
- assertThat(mInfoMediaManager.connectDeviceWithoutPackageName(device)).isFalse();
- }
-
- @Test
public void onRoutesRemoved_getAvailableRoutes_shouldAddMediaDevice() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
@@ -678,17 +671,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void getSessionVolumeMax_routeSessionInfoIsNull_returnNotFound() {
- final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
- final RoutingSessionInfo info = null;
- routingSessionInfos.add(info);
-
- mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
-
- assertThat(mInfoMediaManager.getSessionVolumeMax()).isEqualTo(-1);
- }
-
- @Test
public void getSessionVolume_containPackageName_returnMaxVolume() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -703,17 +685,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void getSessionVolume_routeSessionInfoIsNull_returnNotFound() {
- final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
- final RoutingSessionInfo info = null;
- routingSessionInfos.add(info);
-
- mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
-
- assertThat(mInfoMediaManager.getSessionVolume()).isEqualTo(-1);
- }
-
- @Test
public void getRemoteSessions_returnsRemoteSessions() {
final List<RoutingSessionInfo> infos = new ArrayList<>();
infos.add(mock(RoutingSessionInfo.class));
@@ -735,17 +706,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void getSessionName_routeSessionInfoIsNull_returnNull() {
- final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
- final RoutingSessionInfo info = null;
- routingSessionInfos.add(info);
-
- mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
-
- assertThat(mInfoMediaManager.getSessionName()).isNull();
- }
-
- @Test
public void getSessionName_containPackageName_returnName() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index b58187d8e95e..28cdc6db192b 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -807,7 +807,9 @@ public class SettingsBackupTest {
Settings.Secure.UI_TRANSLATION_ENABLED,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_EDGE_HAPTIC_ENABLED,
Settings.Secure.DND_CONFIGS_MIGRATED,
- Settings.Secure.NAVIGATION_MODE_RESTORE);
+ Settings.Secure.NAVIGATION_MODE_RESTORE,
+ Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
+ Settings.Secure.V_TO_U_RESTORE_DENYLIST);
@Test
public void systemSettingsBackedUpOrDenied() {
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING b/packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING
index 29a25ad04cbe..4a10108b3e04 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING
+++ b/packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING
@@ -1,14 +1,5 @@
{
- "presubmit": [
- {
- "name": "AccessibilityMenuServiceTests",
- "options": [
- {
- "exclude-annotation": "android.support.test.filters.FlakyTest"
- }
- ]
- }
- ],
+ // TODO: b/324945360 - Re-enable on presubmit after fixing failures
"postsubmit": [
{
"name": "AccessibilityMenuServiceTests",
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-sv/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-sv/strings.xml
index ef6966707fda..c02bbb2643d5 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-sv/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-sv/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_menu_service_name" msgid="730136711554740131">"Tillgänglighetsmenyn"</string>
+ <string name="accessibility_menu_service_name" msgid="730136711554740131">"Till- gänglighetsmeny"</string>
<string name="accessibility_menu_intro" msgid="3164193281544042394">"Tillgänglighetsmenyn är en stor meny på skärmen som du kan styra enheten med. Du kan låsa enheten, ställa in volym och ljusstyrka, ta skärmbilder och annat."</string>
<string name="assistant_label" msgid="6796392082252272356">"Assistent"</string>
<string name="assistant_utterance" msgid="65509599221141377">"Assistent"</string>
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 994a020974a1..71f9ba2788a1 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -509,3 +509,23 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "trim_resources_with_background_trim_at_lock"
+ namespace: "systemui"
+ description: "Trim fonts and other caches when the device locks to lower memory consumption."
+ bug: "322143614"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "dedicated_notif_inflation_thread"
+ namespace: "systemui"
+ description: "Create a separate background thread for inflating notifications"
+ bug: "308967184"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/anc/AncModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
new file mode 100644
index 000000000000..a4fb05d3b5b9
--- /dev/null
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc
+
+import dagger.Module
+
+@Module interface AncModule
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index cddd4fa1c33d..622a4f04ee0d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -73,6 +73,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
@@ -85,7 +86,10 @@ import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
@@ -110,6 +114,7 @@ import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.res.R
import kotlinx.coroutines.launch
+@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun CommunalHub(
modifier: Modifier = Modifier,
@@ -140,6 +145,8 @@ fun CommunalHub(
Box(
modifier =
modifier
+ .semantics { testTagsAsResourceId = true }
+ .testTag(COMMUNAL_HUB_TEST_TAG)
.fillMaxSize()
.pointerInput(gridState, contentOffset, contentListState) {
// If not in edit mode, don't allow selecting items.
@@ -872,3 +879,6 @@ object Dimensions {
)
val IconSize = 48.dp
}
+
+/** The resource id of communal hub accessible from UiAutomator. */
+private const val COMMUNAL_HUB_TEST_TAG = "communal_hub"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
index c4184905f28d..7a73c58ba193 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
@@ -73,7 +73,7 @@ private fun rememberBurnInParameters(
BurnInParameters(
clockControllerProvider = { clock },
topInset = topInset,
- statusViewTop = topmostTop,
+ minViewY = topmostTop,
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index ed2cbb85ba1a..c7d43fcdf5e6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -55,7 +55,7 @@ constructor(
notificationStackAppearanceViewModel: NotificationStackAppearanceViewModel,
ambientState: AmbientState,
notificationStackSizeCalculator: NotificationStackSizeCalculator,
- @Main private val mainDispatcher: CoroutineDispatcher,
+ @Main private val mainImmediateDispatcher: CoroutineDispatcher,
) {
init {
@@ -80,7 +80,7 @@ constructor(
sceneContainerFlags,
controller,
notificationStackSizeCalculator,
- mainDispatcher,
+ mainImmediateDispatcher = mainImmediateDispatcher,
)
if (sceneContainerFlags.flexiNotifsEnabled()) {
@@ -90,6 +90,7 @@ constructor(
notificationStackAppearanceViewModel,
ambientState,
controller,
+ mainImmediateDispatcher = mainImmediateDispatcher,
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index 735c43356a40..61b2d4e26097 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -21,8 +21,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.media.controls.ui.MediaCarouselController
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController
+import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.util.animation.MeasurementInput
private object MediaCarousel {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 5006beb01fb4..9779d7170d0d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -78,6 +78,7 @@ fun SceneContainer(
val state: MutableSceneTransitionLayoutState = remember {
MutableSceneTransitionLayoutState(
initialScene = currentSceneKey.asComposeAware(),
+ canChangeScene = { toScene -> viewModel.canChangeScene(toScene.asComposeUnaware()) },
transitions = SceneContainerTransitions,
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt
index 6f115d88dbe2..5c6e1c89ad65 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt
@@ -13,7 +13,10 @@ fun TransitionBuilder.goneToShadeTransition(
) {
spec = tween(durationMillis = DefaultDuration.times(durationScale).inWholeMilliseconds.toInt())
- fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContent) }
+ fractionRange(start = .58f) { fade(ShadeHeader.Elements.Clock) }
+ fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContentStart) }
+ fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContentEnd) }
+ fractionRange(start = .58f) { fade(ShadeHeader.Elements.PrivacyChip) }
translate(QuickSettings.Elements.Content, y = -ShadeHeader.Dimensions.CollapsedHeight * .66f)
translate(Notifications.Elements.NotificationScrim, Edge.Top, false)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
index d5c2a03b3f9f..ffb6f3109015 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
@@ -13,10 +13,20 @@ fun TransitionBuilder.shadeToQuickSettingsTransition() {
translate(Notifications.Elements.NotificationScrim, Edge.Bottom)
timestampRange(endMillis = 83) { fade(QuickSettings.Elements.FooterActions) }
- translate(ShadeHeader.Elements.CollapsedContent, y = ShadeHeader.Dimensions.CollapsedHeight)
- translate(ShadeHeader.Elements.ExpandedContent, y = (-ShadeHeader.Dimensions.ExpandedHeight))
+ translate(
+ ShadeHeader.Elements.CollapsedContentStart,
+ y = ShadeHeader.Dimensions.CollapsedHeight
+ )
+ translate(ShadeHeader.Elements.CollapsedContentEnd, y = ShadeHeader.Dimensions.CollapsedHeight)
+ translate(
+ ShadeHeader.Elements.ExpandedContent,
+ y = -(ShadeHeader.Dimensions.ExpandedHeight - ShadeHeader.Dimensions.CollapsedHeight)
+ )
+ translate(ShadeHeader.Elements.ShadeCarrierGroup, y = -ShadeHeader.Dimensions.CollapsedHeight)
- fractionRange(end = .14f) { fade(ShadeHeader.Elements.CollapsedContent) }
+ fractionRange(end = .14f) { fade(ShadeHeader.Elements.CollapsedContentStart) }
+ fractionRange(end = .14f) { fade(ShadeHeader.Elements.CollapsedContentEnd) }
fractionRange(start = .58f) { fade(ShadeHeader.Elements.ExpandedContent) }
+ fractionRange(start = .58f) { fade(ShadeHeader.Elements.ShadeCarrierGroup) }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index b11edf7b47b7..d7911eac8a61 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -19,7 +19,9 @@ package com.android.systemui.shade.ui.composable
import android.view.ContextThemeWrapper
import android.view.ViewGroup
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
@@ -48,8 +50,10 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.ValueKey
+import com.android.compose.animation.scene.animateElementFloatAsState
import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.settingslib.Utils
@@ -57,9 +61,12 @@ import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
+import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
import com.android.systemui.scene.ui.composable.QuickSettings
import com.android.systemui.scene.ui.composable.Shade as ShadeKey
+import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.CollapsedHeight
+import com.android.systemui.shade.ui.composable.ShadeHeader.Values.ClockScale
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager
@@ -72,13 +79,21 @@ import com.android.systemui.statusbar.policy.Clock
object ShadeHeader {
object Elements {
val ExpandedContent = ElementKey("ShadeHeaderExpandedContent")
- val CollapsedContent = ElementKey("ShadeHeaderCollapsedContent")
+ val CollapsedContentStart = ElementKey("ShadeHeaderCollapsedContentStart")
+ val CollapsedContentEnd = ElementKey("ShadeHeaderCollapsedContentEnd")
+ val PrivacyChip = ElementKey("PrivacyChip", scenePicker = LowestZIndexScenePicker)
+ val Clock = ElementKey("ShadeHeaderClock", scenePicker = LowestZIndexScenePicker)
+ val ShadeCarrierGroup = ElementKey("ShadeCarrierGroup")
}
object Keys {
val transitionProgress = ValueKey("ShadeHeaderTransitionProgress")
}
+ object Values {
+ val ClockScale = ValueKey("ShadeHeaderClockScale")
+ }
+
object Dimensions {
val CollapsedHeight = 48.dp
val ExpandedHeight = 120.dp
@@ -106,58 +121,70 @@ fun SceneScope.CollapsedShadeHeader(
cutoutLocation != CutoutLocation.CENTER || formatProgress.value > 0.5f
}
}
+ val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()
// This layout assumes it is globally positioned at (0, 0) and is the
// same size as the screen.
Layout(
- modifier = modifier.element(ShadeHeader.Elements.CollapsedContent),
+ modifier = modifier,
contents =
listOf(
{
Row {
- AndroidView(
- factory = { context ->
- Clock(
- ContextThemeWrapper(context, R.style.TextAppearance_QS_Status),
- null
- )
- },
+ Clock(
+ scale = 1f,
+ viewModel = viewModel,
modifier = Modifier.align(Alignment.CenterVertically),
)
Spacer(modifier = Modifier.width(5.dp))
VariableDayDate(
viewModel = viewModel,
- modifier = Modifier.align(Alignment.CenterVertically),
+ modifier =
+ Modifier.element(ShadeHeader.Elements.CollapsedContentStart)
+ .align(Alignment.CenterVertically),
)
}
},
{
- Row(horizontalArrangement = Arrangement.End) {
- SystemIconContainer {
- when (LocalWindowSizeClass.current.widthSizeClass) {
- WindowWidthSizeClass.Medium,
- WindowWidthSizeClass.Expanded ->
- ShadeCarrierGroup(
- viewModel = viewModel,
- modifier = Modifier.align(Alignment.CenterVertically),
- )
- }
- StatusIcons(
+ if (isPrivacyChipVisible) {
+ Box(modifier = Modifier.height(CollapsedHeight).fillMaxWidth()) {
+ PrivacyChip(
viewModel = viewModel,
- createTintedIconManager = createTintedIconManager,
- statusBarIconController = statusBarIconController,
- useExpandedFormat = useExpandedFormat,
- modifier =
- Modifier.align(Alignment.CenterVertically)
- .padding(end = 6.dp)
- .weight(1f, fill = false)
- )
- BatteryIcon(
- createBatteryMeterViewController = createBatteryMeterViewController,
- useExpandedFormat = useExpandedFormat,
- modifier = Modifier.align(Alignment.CenterVertically),
+ modifier = Modifier.align(Alignment.CenterEnd),
)
}
+ } else {
+ Row(
+ horizontalArrangement = Arrangement.End,
+ modifier = Modifier.element(ShadeHeader.Elements.CollapsedContentEnd)
+ ) {
+ SystemIconContainer {
+ when (LocalWindowSizeClass.current.widthSizeClass) {
+ WindowWidthSizeClass.Medium,
+ WindowWidthSizeClass.Expanded ->
+ ShadeCarrierGroup(
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.CenterVertically),
+ )
+ }
+ StatusIcons(
+ viewModel = viewModel,
+ createTintedIconManager = createTintedIconManager,
+ statusBarIconController = statusBarIconController,
+ useExpandedFormat = useExpandedFormat,
+ modifier =
+ Modifier.align(Alignment.CenterVertically)
+ .padding(end = 6.dp)
+ .weight(1f, fill = false)
+ )
+ BatteryIcon(
+ createBatteryMeterViewController =
+ createBatteryMeterViewController,
+ useExpandedFormat = useExpandedFormat,
+ modifier = Modifier.align(Alignment.CenterVertically),
+ )
+ }
+ }
}
},
),
@@ -223,72 +250,106 @@ fun SceneScope.ExpandedShadeHeader(
.unsafeCompositionState(initialValue = 1f)
val useExpandedFormat by
remember(formatProgress) { derivedStateOf { formatProgress.value > 0.5f } }
+ val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()
- Column(
- verticalArrangement = Arrangement.Bottom,
- modifier =
- modifier
- .element(ShadeHeader.Elements.ExpandedContent)
- .fillMaxWidth()
- .defaultMinSize(minHeight = ShadeHeader.Dimensions.ExpandedHeight)
- ) {
- Row {
- AndroidView(
- factory = { context ->
- Clock(ContextThemeWrapper(context, R.style.TextAppearance_QS_Status), null)
- },
- modifier =
- Modifier.align(Alignment.CenterVertically)
- // use graphicsLayer instead of Modifier.scale to anchor transform to
- // the (start, top) corner
- .graphicsLayer(
- scaleX = 2.57f,
- scaleY = 2.57f,
- transformOrigin =
- TransformOrigin(
- when (LocalLayoutDirection.current) {
- LayoutDirection.Ltr -> 0f
- LayoutDirection.Rtl -> 1f
- },
- 0.5f
- )
- ),
- )
- Spacer(modifier = Modifier.weight(1f))
- ShadeCarrierGroup(
- viewModel = viewModel,
- modifier = Modifier.align(Alignment.CenterVertically),
- )
- }
- Spacer(modifier = Modifier.width(5.dp))
- Row {
- VariableDayDate(
- viewModel = viewModel,
- modifier = Modifier.widthIn(max = 90.dp).align(Alignment.CenterVertically),
- )
- Spacer(modifier = Modifier.weight(1f))
- SystemIconContainer {
- StatusIcons(
+ Box(modifier = modifier) {
+ if (isPrivacyChipVisible) {
+ Box(modifier = Modifier.height(CollapsedHeight).fillMaxWidth()) {
+ PrivacyChip(
viewModel = viewModel,
- createTintedIconManager = createTintedIconManager,
- statusBarIconController = statusBarIconController,
- useExpandedFormat = useExpandedFormat,
- modifier =
- Modifier.align(Alignment.CenterVertically)
- .padding(end = 6.dp)
- .weight(1f, fill = false),
+ modifier = Modifier.align(Alignment.CenterEnd),
)
- BatteryIcon(
- useExpandedFormat = useExpandedFormat,
- createBatteryMeterViewController = createBatteryMeterViewController,
- modifier = Modifier.align(Alignment.CenterVertically),
+ }
+ }
+ Column(
+ verticalArrangement = Arrangement.Bottom,
+ modifier =
+ Modifier.fillMaxWidth()
+ .defaultMinSize(minHeight = ShadeHeader.Dimensions.ExpandedHeight)
+ ) {
+ Box(modifier = Modifier.fillMaxWidth()) {
+ Box {
+ Clock(
+ scale = 2.57f,
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.CenterStart),
+ )
+ }
+ Box(
+ modifier =
+ Modifier.element(ShadeHeader.Elements.ShadeCarrierGroup).fillMaxWidth()
+ ) {
+ ShadeCarrierGroup(
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.CenterEnd),
+ )
+ }
+ }
+ Spacer(modifier = Modifier.width(5.dp))
+ Row(modifier = Modifier.element(ShadeHeader.Elements.ExpandedContent)) {
+ VariableDayDate(
+ viewModel = viewModel,
+ modifier = Modifier.widthIn(max = 90.dp).align(Alignment.CenterVertically),
)
+ Spacer(modifier = Modifier.weight(1f))
+ SystemIconContainer {
+ StatusIcons(
+ viewModel = viewModel,
+ createTintedIconManager = createTintedIconManager,
+ statusBarIconController = statusBarIconController,
+ useExpandedFormat = useExpandedFormat,
+ modifier =
+ Modifier.align(Alignment.CenterVertically)
+ .padding(end = 6.dp)
+ .weight(1f, fill = false),
+ )
+ BatteryIcon(
+ useExpandedFormat = useExpandedFormat,
+ createBatteryMeterViewController = createBatteryMeterViewController,
+ modifier = Modifier.align(Alignment.CenterVertically),
+ )
+ }
}
}
}
}
@Composable
+private fun SceneScope.Clock(
+ scale: Float,
+ viewModel: ShadeHeaderViewModel,
+ modifier: Modifier,
+) {
+ val layoutDirection = LocalLayoutDirection.current
+
+ Element(key = ShadeHeader.Elements.Clock, modifier = modifier) {
+ val animatedScale by animateElementFloatAsState(scale, ClockScale, canOverflow = false)
+ AndroidView(
+ factory = { context ->
+ Clock(ContextThemeWrapper(context, R.style.TextAppearance_QS_Status), null)
+ },
+ modifier =
+ modifier
+ // use graphicsLayer instead of Modifier.scale to anchor transform
+ // to the (start, top) corner
+ .graphicsLayer {
+ scaleX = animatedScale
+ scaleY = animatedScale
+ transformOrigin =
+ TransformOrigin(
+ when (layoutDirection) {
+ LayoutDirection.Ltr -> 0f
+ LayoutDirection.Rtl -> 1f
+ },
+ 0.5f
+ )
+ }
+ .clickable { viewModel.onClockClicked() }
+ )
+ }
+}
+
+@Composable
private fun BatteryIcon(
createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
useExpandedFormat: Boolean,
@@ -359,7 +420,14 @@ private fun SceneScope.StatusIcons(
) {
val carrierIconSlots =
listOf(stringResource(id = com.android.internal.R.string.status_bar_mobile))
+ val cameraSlot = stringResource(id = com.android.internal.R.string.status_bar_camera)
+ val micSlot = stringResource(id = com.android.internal.R.string.status_bar_microphone)
+ val locationSlot = stringResource(id = com.android.internal.R.string.status_bar_location)
+
val isSingleCarrier by viewModel.isSingleCarrier.collectAsState()
+ val isPrivacyChipEnabled by viewModel.isPrivacyChipEnabled.collectAsState()
+ val isMicCameraIndicationEnabled by viewModel.isMicCameraIndicationEnabled.collectAsState()
+ val isLocationIndicationEnabled by viewModel.isLocationIndicationEnabled.collectAsState()
AndroidView(
factory = { context ->
@@ -382,6 +450,25 @@ private fun SceneScope.StatusIcons(
} else {
iconContainer.addIgnoredSlots(carrierIconSlots)
}
+
+ if (isPrivacyChipEnabled) {
+ if (isMicCameraIndicationEnabled) {
+ iconContainer.addIgnoredSlot(cameraSlot)
+ iconContainer.addIgnoredSlot(micSlot)
+ } else {
+ iconContainer.removeIgnoredSlot(cameraSlot)
+ iconContainer.removeIgnoredSlot(micSlot)
+ }
+ if (isLocationIndicationEnabled) {
+ iconContainer.addIgnoredSlot(locationSlot)
+ } else {
+ iconContainer.removeIgnoredSlot(locationSlot)
+ }
+ } else {
+ iconContainer.removeIgnoredSlot(cameraSlot)
+ iconContainer.removeIgnoredSlot(micSlot)
+ iconContainer.removeIgnoredSlot(locationSlot)
+ }
},
modifier = modifier,
)
@@ -394,7 +481,28 @@ private fun SystemIconContainer(
) {
// TODO(b/298524053): add hover state for this container
Row(
- modifier = modifier.height(ShadeHeader.Dimensions.CollapsedHeight),
+ modifier = modifier.height(CollapsedHeight),
content = content,
)
}
+
+@Composable
+private fun SceneScope.PrivacyChip(
+ viewModel: ShadeHeaderViewModel,
+ modifier: Modifier = Modifier,
+) {
+ val privacyList by viewModel.privacyItems.collectAsState()
+
+ AndroidView(
+ factory = { context ->
+ val view =
+ OngoingPrivacyChip(context, null).also { privacyChip ->
+ privacyChip.privacyList = privacyList
+ privacyChip.setOnClickListener { viewModel.onPrivacyChipClicked(privacyChip) }
+ }
+ view
+ },
+ update = { it.privacyList = privacyList },
+ modifier = modifier.element(ShadeHeader.Elements.PrivacyChip),
+ )
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 8c6bf618454c..2e0ce42ee713 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -42,14 +43,15 @@ import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.modifiers.thenIf
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.media.controls.ui.MediaCarouselController
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.media.controls.ui.MediaHostState
import com.android.systemui.media.controls.ui.composable.MediaCarousel
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
import com.android.systemui.notifications.ui.composable.NotificationScrollingStack
import com.android.systemui.qs.ui.composable.QuickSettings
@@ -163,6 +165,7 @@ private fun SceneScope.ShadeScene(
val maxNotifScrimTop = remember { mutableStateOf(0f) }
val tileSquishiness by
animateSceneFloatAsState(value = 1f, key = QuickSettings.SharedValues.TilesSquishiness)
+ val isClickable by viewModel.isClickable.collectAsState()
Box(
modifier =
@@ -178,8 +181,9 @@ private fun SceneScope.ShadeScene(
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier =
- Modifier.fillMaxWidth()
- .clickable(onClick = { viewModel.onContentClicked() })
+ Modifier.fillMaxWidth().thenIf(isClickable) {
+ Modifier.clickable(onClick = { viewModel.onContentClicked() })
+ }
) {
CollapsedShadeHeader(
viewModel = viewModel.shadeHeaderViewModel,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
index 7d692cc17015..d66bada04297 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone
import android.content.Context
+import androidx.annotation.GravityInt
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -45,14 +46,17 @@ import com.android.compose.theme.PlatformTheme
* @param context the [Context] in which the dialog will be constructed.
* @param dismissOnDeviceLock whether the dialog should be automatically dismissed when the device
* is locked (true by default).
+ * @param dialogGravity is one of the [android.view.Gravity] and determines dialog position on the
+ * screen.
*/
fun SystemUIDialogFactory.create(
context: Context = this.applicationContext,
theme: Int = SystemUIDialog.DEFAULT_THEME,
dismissOnDeviceLock: Boolean = SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK,
+ @GravityInt dialogGravity: Int? = null,
content: @Composable (SystemUIDialog) -> Unit,
): ComponentSystemUIDialog {
- val dialog = create(context, theme, dismissOnDeviceLock)
+ val dialog = create(context, theme, dismissOnDeviceLock, dialogGravity)
// Create the dialog so that it is properly constructed before we set the Compose content.
// Otherwise, the ComposeView won't render properly.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
new file mode 100644
index 000000000000..ccb5d367c357
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc
+
+import com.android.systemui.volume.panel.component.anc.domain.AncAvailabilityCriteria
+import com.android.systemui.volume.panel.component.anc.ui.composable.AncPopup
+import com.android.systemui.volume.panel.component.anc.ui.viewmodel.AncViewModel
+import com.android.systemui.volume.panel.component.button.ui.composable.ButtonComponent
+import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents
+import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+/** Dagger module, that provides Active Noise Cancellation Volume Panel UI functionality. */
+@Module
+interface AncModule {
+
+ @Binds
+ @IntoMap
+ @StringKey(VolumePanelComponents.ANC)
+ fun bindComponentAvailabilityCriteria(
+ criteria: AncAvailabilityCriteria
+ ): ComponentAvailabilityCriteria
+
+ companion object {
+
+ @Provides
+ @IntoMap
+ @StringKey(VolumePanelComponents.ANC)
+ fun provideVolumePanelUiComponent(
+ viewModel: AncViewModel,
+ popup: AncPopup,
+ ): VolumePanelUiComponent = ButtonComponent(viewModel.button, popup::show)
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
new file mode 100644
index 000000000000..8ac84ff819eb
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc.ui.composable
+
+import android.content.Context
+import android.view.View
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.slice.Slice
+import androidx.slice.widget.SliceView
+import com.android.systemui.animation.Expandable
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.volume.panel.component.anc.ui.viewmodel.AncViewModel
+import com.android.systemui.volume.panel.component.popup.ui.composable.VolumePanelPopup
+import javax.inject.Inject
+
+/** ANC popup up displaying ANC control [Slice]. */
+class AncPopup
+@Inject
+constructor(
+ private val volumePanelPopup: VolumePanelPopup,
+ private val viewModel: AncViewModel,
+) {
+
+ /** Shows a popup with the [expandable] animation. */
+ fun show(expandable: Expandable) {
+ volumePanelPopup.show(expandable, { Title() }, { Content(it) })
+ }
+
+ @Composable
+ private fun Title() {
+ Text(
+ text = stringResource(R.string.volume_panel_noise_control_title),
+ style = MaterialTheme.typography.titleMedium,
+ textAlign = TextAlign.Center,
+ maxLines = 1,
+ )
+ }
+
+ @Composable
+ private fun Content(dialog: SystemUIDialog) {
+ val slice: Slice? by viewModel.slice.collectAsState()
+
+ if (slice == null) {
+ SideEffect { dialog.dismiss() }
+ return
+ }
+
+ AndroidView<SliceView>(
+ modifier = Modifier.fillMaxWidth(),
+ factory = { context: Context ->
+ SliceView(context).apply {
+ mode = SliceView.MODE_LARGE
+ isScrollable = false
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ setShowTitleItems(true)
+ addOnLayoutChangeListener(
+ OnWidthChangedLayoutListener(viewModel::changeSliceWidth)
+ )
+ }
+ },
+ update = { sliceView: SliceView -> sliceView.slice = slice }
+ )
+ }
+
+ private class OnWidthChangedLayoutListener(private val widthChanged: (Int) -> Unit) :
+ View.OnLayoutChangeListener {
+ override fun onLayoutChange(
+ v: View?,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ val newWidth = right - left
+ val oldWidth = oldRight - oldLeft
+ if (oldWidth != newWidth) {
+ widthChanged(newWidth)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt
index 0cf43672c716..d40126198c33 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt
@@ -47,7 +47,7 @@ constructor(
@Composable
override fun VolumePanelComposeScope.Content(modifier: Modifier) {
Row(
- modifier = modifier.height(48.dp).fillMaxWidth(),
+ modifier = modifier.height(if (isLargeScreen) 54.dp else 48.dp).fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
new file mode 100644
index 000000000000..5f7bd47f2a1b
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.button.ui.composable
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.Expandable
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
+import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
+import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
+import kotlinx.coroutines.flow.StateFlow
+
+/** [ComposeVolumePanelUiComponent] implementing a clickable button from a bottom row. */
+class ButtonComponent(
+ private val viewModelFlow: StateFlow<ButtonViewModel?>,
+ private val onClick: (Expandable) -> Unit
+) : ComposeVolumePanelUiComponent {
+
+ @Composable
+ override fun VolumePanelComposeScope.Content(modifier: Modifier) {
+ val viewModelByState by viewModelFlow.collectAsState()
+ val viewModel = viewModelByState ?: return
+
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Expandable(
+ modifier = Modifier.height(64.dp).fillMaxWidth(),
+ color = MaterialTheme.colorScheme.primaryContainer,
+ shape = RoundedCornerShape(28.dp),
+ contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ borderStroke = BorderStroke(8.dp, MaterialTheme.colorScheme.surface),
+ onClick = onClick,
+ ) {
+ Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+ Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon)
+ }
+ }
+ Text(
+ text = viewModel.label.toString(),
+ style = MaterialTheme.typography.labelMedium,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
index 228111dae8d9..dfee684c417f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
@@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedIconToggleButton
@@ -39,6 +40,7 @@ import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiCompo
import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
import kotlinx.coroutines.flow.StateFlow
+/** [ComposeVolumePanelUiComponent] implementing a toggleable button from a bottom row. */
class ToggleButtonComponent(
private val viewModelFlow: StateFlow<ToggleButtonViewModel?>,
private val onCheckedChange: (isChecked: Boolean) -> Unit
@@ -57,6 +59,7 @@ class ToggleButtonComponent(
modifier = Modifier.height(64.dp).fillMaxWidth(),
checked = viewModel.isChecked,
onCheckedChange = onCheckedChange,
+ shape = RoundedCornerShape(28.dp),
colors =
IconButtonDefaults.outlinedIconToggleButtonColors(
containerColor = MaterialTheme.colorScheme.surface,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
index 8ad6fdf829d7..d49fed5d6e10 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
@@ -167,6 +167,7 @@ constructor(
) {
Icon(
icon = it.icon,
+ tint = it.iconColor.toColor(),
modifier = Modifier.padding(12.dp).fillMaxSize(),
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
new file mode 100644
index 000000000000..89251939f77a
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.popup.ui.composable
+
+import android.view.Gravity
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.material3.IconButtonDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.android.compose.PlatformIconButton
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.SystemUIDialogFactory
+import com.android.systemui.statusbar.phone.create
+import javax.inject.Inject
+
+/** Volume panel bottom popup menu. */
+class VolumePanelPopup
+@Inject
+constructor(
+ private val dialogFactory: SystemUIDialogFactory,
+ private val dialogTransitionAnimator: DialogTransitionAnimator,
+) {
+
+ /**
+ * Shows a popup with the [expandable] animation.
+ *
+ * @param title is shown on the top of the popup
+ * @param content is the popup body
+ */
+ fun show(
+ expandable: Expandable,
+ title: @Composable (SystemUIDialog) -> Unit,
+ content: @Composable (SystemUIDialog) -> Unit,
+ ) {
+ val dialog =
+ dialogFactory.create(
+ theme = R.style.Theme_VolumePanelActivity_Popup,
+ dialogGravity = Gravity.BOTTOM,
+ ) {
+ PopupComposable(it, title, content)
+ }
+ val controller = expandable.dialogTransitionController()
+ if (controller == null) {
+ dialog.show()
+ } else {
+ dialogTransitionAnimator.show(dialog, controller)
+ }
+ }
+
+ @Composable
+ private fun PopupComposable(
+ dialog: SystemUIDialog,
+ title: @Composable (SystemUIDialog) -> Unit,
+ content: @Composable (SystemUIDialog) -> Unit,
+ ) {
+ Box(Modifier.fillMaxWidth()) {
+ Column(
+ modifier = Modifier.fillMaxWidth().padding(vertical = 20.dp),
+ verticalArrangement = Arrangement.spacedBy(20.dp),
+ ) {
+ Box(
+ modifier =
+ Modifier.padding(horizontal = 80.dp).fillMaxWidth().wrapContentHeight(),
+ contentAlignment = Alignment.Center
+ ) {
+ title(dialog)
+ }
+
+ Box(
+ modifier =
+ Modifier.padding(horizontal = 16.dp).fillMaxWidth().wrapContentHeight(),
+ contentAlignment = Alignment.Center
+ ) {
+ content(dialog)
+ }
+ }
+
+ PlatformIconButton(
+ modifier = Modifier.align(Alignment.TopEnd).size(64.dp).padding(20.dp),
+ iconResource = R.drawable.ic_close,
+ contentDescription = null,
+ onClick = { dialog.dismiss() },
+ colors =
+ IconButtonDefaults.iconButtonColors(
+ contentColor = MaterialTheme.colorScheme.outline
+ )
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
index 86eb84929c02..22851286354c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
@@ -52,7 +52,7 @@ fun VolumePanelComposeScope.VerticalVolumePanelContent(
if (layout.footerComponents.isNotEmpty()) {
Row(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
- horizontalArrangement = Arrangement.spacedBy(20.dp),
+ horizontalArrangement = Arrangement.spacedBy(if (isLargeScreen) 28.dp else 20.dp),
) {
for (component in layout.footerComponents) {
AnimatedVisibility(component.isVisible) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt
index 10731c7f2df7..8df8d2e81b51 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt
@@ -27,8 +27,8 @@ class VolumePanelComposeScope(private val state: VolumePanelState) {
val orientation: Int
get() = state.orientation
- /** Is true when Volume Panel is using wide-screen layout and false the otherwise. */
- val isWideScreen: Boolean
+ /** Is true when Volume Panel is using large-screen layout and false the otherwise. */
+ val isLargeScreen: Boolean
get() = state.isWideScreen
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
index dd6342029885..8a1e6a8b74f4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
@@ -19,6 +19,7 @@ package com.android.systemui.volume.panel.ui.composable
import android.content.res.Configuration
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@@ -26,6 +27,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
@@ -43,6 +45,8 @@ import com.android.systemui.volume.panel.ui.layout.ComponentsLayout
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelState
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
+private val padding = 24.dp
+
@Composable
fun VolumePanelRoot(
viewModel: VolumePanelViewModel,
@@ -84,7 +88,18 @@ fun VolumePanelRoot(
shape = RoundedCornerShape(topStart = radius, topEnd = radius),
color = MaterialTheme.colorScheme.surfaceContainer,
) {
- Column { components?.let { componentsState -> Components(componentsState) } }
+ components?.let { componentsState ->
+ Components(
+ componentsState,
+ Modifier.padding(
+ start = padding,
+ top = padding,
+ end = padding,
+ bottom = 20.dp,
+ )
+ .navigationBarsPadding()
+ )
+ }
}
}
}
@@ -92,36 +107,35 @@ fun VolumePanelRoot(
}
@Composable
-private fun VolumePanelComposeScope.Components(components: ComponentsLayout) {
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- VerticalVolumePanelContent(
- components,
- modifier = Modifier.padding(24.dp),
- )
- } else {
- HorizontalVolumePanelContent(
- components,
- modifier =
- Modifier.padding(start = 24.dp, top = 24.dp, end = 24.dp, bottom = 20.dp)
- .heightIn(max = 236.dp),
- )
+private fun VolumePanelComposeScope.Components(
+ layout: ComponentsLayout,
+ modifier: Modifier = Modifier
+) {
+ var columnModifier = modifier.widthIn(max = 800.dp)
+ if (!isLargeScreen && orientation != Configuration.ORIENTATION_PORTRAIT) {
+ columnModifier = columnModifier.heightIn(max = 332.dp)
+ }
+ Column(modifier = columnModifier, verticalArrangement = Arrangement.spacedBy(padding)) {
+ if (orientation == Configuration.ORIENTATION_PORTRAIT || isLargeScreen) {
+ VerticalVolumePanelContent(layout)
+ } else {
+ HorizontalVolumePanelContent(layout)
+ }
+ BottomBar(layout = layout, modifier = Modifier)
}
+}
- if (components.bottomBarComponent.isVisible) {
- val horizontalPadding =
- dimensionResource(R.dimen.volume_panel_bottom_bar_horizontal_padding)
+@Composable
+private fun VolumePanelComposeScope.BottomBar(
+ layout: ComponentsLayout,
+ modifier: Modifier = Modifier
+) {
+ if (layout.bottomBarComponent.isVisible) {
Box(
- modifier =
- Modifier.fillMaxWidth()
- .navigationBarsPadding()
- .padding(
- start = horizontalPadding,
- end = horizontalPadding,
- bottom = dimensionResource(R.dimen.volume_panel_bottom_bar_bottom_padding),
- ),
+ modifier = modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
- with(components.bottomBarComponent.component as ComposeVolumePanelUiComponent) {
+ with(layout.bottomBarComponent.component as ComposeVolumePanelUiComponent) {
Content(Modifier)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
index 059620502a49..b31f6f5b096b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
@@ -220,6 +220,7 @@ class FaceHelpMessageDeferralTest : SysuiTestCase() {
threshold,
logger,
dumpManager,
+ "0",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
index 45f98be2ca12..1cdc2b69034b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
@@ -21,8 +21,8 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
index 5b20ae5131b2..06b3806cb382 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
@@ -25,7 +25,6 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
@@ -57,46 +56,6 @@ class CommunalRepositoryImplTest : SysuiTestCase() {
}
@Test
- fun isCommunalShowing_sceneContainerDisabled_onCommunalScene_true() =
- testScope.runTest {
- underTest.setDesiredScene(CommunalSceneKey.Communal)
-
- val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
- assertThat(isCommunalHubShowing).isTrue()
- }
-
- @Test
- fun isCommunalShowing_sceneContainerDisabled_onBlankScene_false() =
- testScope.runTest {
- underTest.setDesiredScene(CommunalSceneKey.Blank)
-
- val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
- assertThat(isCommunalHubShowing).isFalse()
- }
-
- @Test
- fun isCommunalShowing_sceneContainerEnabled_onCommunalScene_true() =
- testScope.runTest {
- underTest = createRepositoryImpl(true)
-
- sceneContainerRepository.changeScene(SceneKey.Communal)
-
- val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
- assertThat(isCommunalHubShowing).isTrue()
- }
-
- @Test
- fun isCommunalShowing_sceneContainerEnabled_onLockscreenScene_false() =
- testScope.runTest {
- underTest = createRepositoryImpl(true)
-
- sceneContainerRepository.changeScene(SceneKey.Lockscreen)
-
- val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
- assertThat(isCommunalHubShowing).isFalse()
- }
-
- @Test
fun transitionState_idleByDefault() =
testScope.runTest {
val transitionState by collectLastValue(underTest.transitionState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
index 824733bcc47b..5a7cbf6e02ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
@@ -67,7 +67,7 @@ class CommunalInteractorCommunalDisabledTest : SysuiTestCase() {
@Test
fun isCommunalEnabled_false() =
- testScope.runTest { assertThat(underTest.isCommunalEnabled).isFalse() }
+ testScope.runTest { assertThat(underTest.isCommunalEnabled.value).isFalse() }
@Test
fun isCommunalAvailable_whenStorageUnlock_false() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 3ac19e4672c3..4156d833b0de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -47,6 +47,10 @@ import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
+import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
import com.android.systemui.testKosmos
@@ -91,6 +95,7 @@ class CommunalInteractorTest : SysuiTestCase() {
private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository
private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter
+ private lateinit var sceneInteractor: SceneInteractor
private lateinit var underTest: CommunalInteractor
@@ -107,6 +112,7 @@ class CommunalInteractorTest : SysuiTestCase() {
keyguardRepository = kosmos.fakeKeyguardRepository
editWidgetsActivityStarter = kosmos.editWidgetsActivityStarter
communalPrefsRepository = kosmos.fakeCommunalPrefsRepository
+ sceneInteractor = kosmos.sceneInteractor
whenever(mainUser.isMain).thenReturn(true)
whenever(secondaryUser.isMain).thenReturn(false)
@@ -123,7 +129,7 @@ class CommunalInteractorTest : SysuiTestCase() {
testScope.runTest {
userRepository.setSelectedUserInfo(mainUser)
runCurrent()
- assertThat(underTest.isCommunalEnabled).isTrue()
+ assertThat(underTest.isCommunalEnabled.value).isTrue()
}
@Test
@@ -598,17 +604,53 @@ class CommunalInteractorTest : SysuiTestCase() {
}
@Test
- fun isCommunalShowing() =
+ fun isCommunalShowing_whenSceneContainerDisabled() =
testScope.runTest {
- var isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
+ // Verify default is false
+ val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
runCurrent()
- assertThat(isCommunalShowing()).isEqualTo(false)
+ assertThat(isCommunalShowing).isFalse()
+ // Verify scene changes with the flag doesn't have any impact
+ sceneInteractor.changeScene(SceneKey.Communal, loggingReason = "")
+ runCurrent()
+ assertThat(isCommunalShowing).isFalse()
+
+ // Verify scene changes (without the flag) to communal sets the value to true
+ underTest.onSceneChanged(CommunalSceneKey.Communal)
+ runCurrent()
+ assertThat(isCommunalShowing).isTrue()
+
+ // Verify scene changes (without the flag) to blank sets the value back to false
+ underTest.onSceneChanged(CommunalSceneKey.Blank)
+ runCurrent()
+ assertThat(isCommunalShowing).isFalse()
+ }
+
+ @Test
+ fun isCommunalShowing_whenSceneContainerEnabled() =
+ testScope.runTest {
+ kosmos.fakeSceneContainerFlags.enabled = true
+
+ // Verify default is false
+ val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
+ runCurrent()
+ assertThat(isCommunalShowing).isFalse()
+
+ // Verify scene changes without the flag doesn't have any impact
underTest.onSceneChanged(CommunalSceneKey.Communal)
+ runCurrent()
+ assertThat(isCommunalShowing).isFalse()
+
+ // Verify scene changes (with the flag) to communal sets the value to true
+ sceneInteractor.changeScene(SceneKey.Communal, loggingReason = "")
+ runCurrent()
+ assertThat(isCommunalShowing).isTrue()
- isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
+ // Verify scene changes (with the flag) to lockscreen sets the value to false
+ sceneInteractor.changeScene(SceneKey.Lockscreen, loggingReason = "")
runCurrent()
- assertThat(isCommunalShowing()).isEqualTo(true)
+ assertThat(isCommunalShowing).isFalse()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
index ceb7fac1046f..5211c55ac911 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -24,10 +24,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
+import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -38,13 +37,11 @@ import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class CommunalTutorialInteractorTest : SysuiTestCase() {
@@ -54,7 +51,6 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
private lateinit var underTest: CommunalTutorialInteractor
private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var communalTutorialRepository: FakeCommunalTutorialRepository
- private lateinit var communalRepository: FakeCommunalRepository
private lateinit var communalInteractor: CommunalInteractor
private lateinit var userRepository: FakeUserRepository
@@ -62,7 +58,6 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
fun setUp() {
keyguardRepository = kosmos.fakeKeyguardRepository
communalTutorialRepository = kosmos.fakeCommunalTutorialRepository
- communalRepository = kosmos.fakeCommunalRepository
communalInteractor = kosmos.communalInteractor
userRepository = kosmos.fakeUserRepository
@@ -90,7 +85,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
setCommunalAvailable(true)
keyguardRepository.setKeyguardShowing(true)
keyguardRepository.setKeyguardOccluded(false)
- communalRepository.setIsCommunalHubShowing(false)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
assertThat(isTutorialAvailable).isFalse()
}
@@ -112,7 +107,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
setCommunalAvailable(true)
keyguardRepository.setKeyguardShowing(true)
keyguardRepository.setKeyguardOccluded(false)
- communalRepository.setIsCommunalHubShowing(false)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
assertThat(isTutorialAvailable).isTrue()
}
@@ -124,7 +119,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
setCommunalAvailable(true)
keyguardRepository.setKeyguardShowing(true)
keyguardRepository.setKeyguardOccluded(false)
- communalRepository.setIsCommunalHubShowing(true)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
assertThat(isTutorialAvailable).isTrue()
}
@@ -137,7 +132,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
userRepository.setSelectedUserInfo(MAIN_USER_INFO)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
- communalRepository.setIsCommunalHubShowing(true)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
}
@@ -150,7 +145,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
userRepository.setSelectedUserInfo(MAIN_USER_INFO)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
- communalRepository.setIsCommunalHubShowing(true)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
}
@@ -163,7 +158,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
userRepository.setSelectedUserInfo(MAIN_USER_INFO)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
- communalRepository.setIsCommunalHubShowing(true)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
}
@@ -176,7 +171,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
userRepository.setSelectedUserInfo(MAIN_USER_INFO)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
- communalRepository.setIsCommunalHubShowing(false)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
}
@@ -187,10 +182,10 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
val tutorialSettingState by
collectLastValue(communalTutorialRepository.tutorialSettingState)
userRepository.setSelectedUserInfo(MAIN_USER_INFO)
- communalRepository.setIsCommunalHubShowing(true)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
- communalRepository.setIsCommunalHubShowing(false)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
}
@@ -201,10 +196,10 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
val tutorialSettingState by
collectLastValue(communalTutorialRepository.tutorialSettingState)
userRepository.setSelectedUserInfo(MAIN_USER_INFO)
- communalRepository.setIsCommunalHubShowing(true)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
- communalRepository.setIsCommunalHubShowing(false)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index ddb8582913e6..352bacc56ca5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -38,7 +38,7 @@ import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
import com.android.systemui.testKosmos
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index b299ca7ee804..cc322d085acd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -43,8 +43,8 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
index f6c056698967..fb46ed9d54ed 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -175,6 +175,21 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() {
animatorTestRule.advanceTimeBy(500L)
assertEquals(1.0f, value)
}
+
+ @Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ fun revealAmount_startingRevealTwiceWontRerunAnimator() =
+ runTest(UnconfinedTestDispatcher()) {
+ val value by collectLastValue(underTest.revealAmount)
+ underTest.startRevealAmountAnimator(true)
+ assertEquals(0.0f, value)
+ animatorTestRule.advanceTimeBy(250L)
+ assertEquals(0.5f, value)
+ underTest.startRevealAmountAnimator(true)
+ animatorTestRule.advanceTimeBy(250L)
+ assertEquals(1.0f, value)
+ }
+
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
fun revealAmount_emitsTo0AfterAnimationStartedReversed() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 9b302ae60e21..2b6e6c7c1575 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -22,7 +22,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.fakeLightRevealScrimRepository
import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
-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.kosmos.testScope
@@ -33,16 +32,11 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
@@ -103,41 +97,4 @@ class LightRevealScrimInteractorTest : SysuiTestCase() {
job.cancel()
}
-
- @Test
- fun lightRevealEffect_startsAnimationOnlyForDifferentStateTargets() =
- testScope.runTest {
- runCurrent()
- reset(fakeLightRevealScrimRepository)
-
- fakeKeyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- from = KeyguardState.OFF,
- to = KeyguardState.OFF
- )
- )
- runCurrent()
- verify(fakeLightRevealScrimRepository, never()).startRevealAmountAnimator(anyBoolean())
-
- fakeKeyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- from = KeyguardState.DOZING,
- to = KeyguardState.LOCKSCREEN
- )
- )
- runCurrent()
- verify(fakeLightRevealScrimRepository).startRevealAmountAnimator(true)
-
- fakeKeyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.DOZING
- )
- )
- runCurrent()
- verify(fakeLightRevealScrimRepository).startRevealAmountAnimator(false)
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index 74fa46519629..b0f59fe68f11 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -127,7 +127,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
@Test
fun translationAndScale_whenFullyDozing() =
testScope.runTest {
- burnInParameters = burnInParameters.copy(statusViewTop = 100)
+ burnInParameters = burnInParameters.copy(minViewY = 100)
val translationX by collectLastValue(underTest.translationX(burnInParameters))
val translationY by collectLastValue(underTest.translationY(burnInParameters))
val scale by collectLastValue(underTest.scale(burnInParameters))
@@ -182,11 +182,77 @@ class AodBurnInViewModelTest : SysuiTestCase() {
}
@Test
- fun translationAndScale_whenFullyDozing_staysOutOfTopInset() =
+ fun translationAndScale_whenFullyDozing_MigrationFlagOff_staysOutOfTopInset() =
testScope.runTest {
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+
+ burnInParameters =
+ burnInParameters.copy(
+ minViewY = 100,
+ topInset = 80,
+ )
+ val translationX by collectLastValue(underTest.translationX(burnInParameters))
+ val translationY by collectLastValue(underTest.translationY(burnInParameters))
+ val scale by collectLastValue(underTest.scale(burnInParameters))
+
+ // Set to dozing (on AOD)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED
+ ),
+ validateStep = false,
+ )
+
+ // Trigger a change to the burn-in model
+ burnInFlow.value =
+ BurnInModel(
+ translationX = 20,
+ translationY = -30,
+ scale = 0.5f,
+ )
+ assertThat(translationX).isEqualTo(20)
+ // -20 instead of -30, due to inset of 80
+ assertThat(translationY).isEqualTo(-20)
+ assertThat(scale)
+ .isEqualTo(
+ BurnInScaleViewModel(
+ scale = 0.5f,
+ scaleClockOnly = true,
+ )
+ )
+
+ // Set to the beginning of GONE->AOD transition
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = 0f,
+ transitionState = TransitionState.STARTED
+ ),
+ validateStep = false,
+ )
+ assertThat(translationX).isEqualTo(0)
+ assertThat(translationY).isEqualTo(0)
+ assertThat(scale)
+ .isEqualTo(
+ BurnInScaleViewModel(
+ scale = 1f,
+ scaleClockOnly = true,
+ )
+ )
+ }
+
+ @Test
+ fun translationAndScale_whenFullyDozing_MigrationFlagOn_staysOutOfTopInset() =
+ testScope.runTest {
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+
burnInParameters =
burnInParameters.copy(
- statusViewTop = 100,
+ minViewY = 100,
topInset = 80,
)
val translationX by collectLastValue(underTest.translationX(burnInParameters))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
index c381749ec6d3..31b67b43adc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
@@ -27,6 +27,7 @@ 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.kosmos.testScope
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,6 +43,7 @@ class AodToLockscreenTransitionViewModelTest : SysuiTestCase() {
val kosmos = testKosmos()
val testScope = kosmos.testScope
val repository = kosmos.fakeKeyguardTransitionRepository
+ val shadeRepository = kosmos.fakeShadeRepository
val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
val underTest = kosmos.aodToLockscreenTransitionViewModel
@@ -59,6 +61,38 @@ class AodToLockscreenTransitionViewModelTest : SysuiTestCase() {
}
@Test
+ fun notificationAlpha_whenShadeIsExpanded_equalsOne() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.notificationAlpha)
+
+ shadeRepository.setQsExpansion(0.5f)
+ runCurrent()
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(alpha).isEqualTo(1f)
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(alpha).isEqualTo(1f)
+ repository.sendTransitionStep(step(1f))
+ assertThat(alpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun notificationAlpha_whenShadeIsNotExpanded_usesTransitionValue() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.notificationAlpha)
+
+ shadeRepository.setQsExpansion(0f)
+ runCurrent()
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(alpha).isEqualTo(0f)
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(alpha).isEqualTo(0.5f)
+ repository.sendTransitionStep(step(1f))
+ assertThat(alpha).isEqualTo(1f)
+ }
+
+ @Test
fun lockscreenAlphaStartsFromViewStateAccessorAlpha() =
testScope.runTest {
val viewState = ViewStateAccessor(alpha = { 0.5f })
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index e04cbfd88bb3..503fd34ce2c2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -39,6 +39,7 @@ 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.kosmos.testScope
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.dozeParameters
import com.android.systemui.statusbar.phone.screenOffAnimationController
@@ -72,6 +73,7 @@ class KeyguardRootViewModelTest : SysuiTestCase() {
private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository
private val notificationsKeyguardInteractor = kosmos.notificationsKeyguardInteractor
private val dozeParameters = kosmos.dozeParameters
+ private val shadeRepository = kosmos.fakeShadeRepository
private val underTest by lazy { kosmos.keyguardRootViewModel }
private val viewState = ViewStateAccessor()
@@ -308,4 +310,22 @@ class KeyguardRootViewModelTest : SysuiTestCase() {
assertThat(alpha).isEqualTo(1.0f)
}
+
+ @Test
+ fun alpha_emitsOnShadeExpansion() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha(viewState))
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+
+ shadeRepository.setQsExpansion(0f)
+ assertThat(alpha).isEqualTo(1f)
+
+ shadeRepository.setQsExpansion(0.5f)
+ assertThat(alpha).isEqualTo(0f)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesResourceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesResourceRepositoryTest.kt
new file mode 100644
index 000000000000..62c91633ea81
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesResourceRepositoryTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.pipeline.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MinimumTilesResourceRepositoryTest : SysuiTestCase() {
+
+ val testableResources = context.orCreateTestableResources
+
+ @Test
+ fun minimumQSTiles_followsConfig() {
+ val minTwo = 2
+ testableResources.addOverride(R.integer.quick_settings_min_num_tiles, minTwo)
+ val underTest = MinimumTilesResourceRepository(context.resources)
+ assertThat(underTest.minNumberOfTiles).isEqualTo(minTwo)
+
+ val minSix = 6
+ testableResources.addOverride(R.integer.quick_settings_min_num_tiles, minSix)
+ val otherUnderTest = MinimumTilesResourceRepository(context.resources)
+ assertThat(otherUnderTest.minNumberOfTiles).isEqualTo(minSix)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
index ff8a9bd019fb..39851b6f21a7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
@@ -8,6 +8,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.FakeBroadcastDispatcher
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.statusbar.policy.FakeDeviceProvisionedController
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -28,6 +29,7 @@ class QSSettingsRestoredBroadcastRepositoryTest : SysuiTestCase() {
private val testScope = TestScope(dispatcher)
@Mock private lateinit var pipelineLogger: QSPipelineLogger
+ private val deviceProvisionedController = FakeDeviceProvisionedController()
private lateinit var underTest: QSSettingsRestoredBroadcastRepository
@@ -38,6 +40,7 @@ class QSSettingsRestoredBroadcastRepositoryTest : SysuiTestCase() {
underTest =
QSSettingsRestoredBroadcastRepository(
fakeBroadcastDispatcher,
+ deviceProvisionedController,
pipelineLogger,
testScope.backgroundScope,
dispatcher,
@@ -176,6 +179,100 @@ class QSSettingsRestoredBroadcastRepositoryTest : SysuiTestCase() {
}
}
+ @Test
+ fun restoreAfterUserSetup_singleTilesRestoredBroadcast() =
+ testScope.runTest {
+ runCurrent()
+ val restoreData by collectLastValue(underTest.restoreData)
+ val user = 0
+
+ val tilesIntent =
+ createRestoreIntent(
+ RestoreType.TILES,
+ CURRENT_TILES,
+ RESTORED_TILES,
+ )
+
+ sendIntentForUser(tilesIntent, user)
+
+ deviceProvisionedController.setUserSetup(user)
+
+ with(restoreData!!) {
+ assertThat(restoredTiles).isEqualTo(RESTORED_TILES.toTilesList())
+ assertThat(restoredAutoAddedTiles).isEmpty()
+ assertThat(userId).isEqualTo(user)
+ }
+ }
+
+ @Test
+ fun restoreAfterUserSetup_singleAutoAddRestoredBroadcast_noRestore() =
+ testScope.runTest {
+ runCurrent()
+ val restoreData by collectLastValue(underTest.restoreData)
+ val user = 0
+
+ val autoAddIntent =
+ createRestoreIntent(
+ RestoreType.AUTOADD,
+ CURRENT_AUTO_ADDED_TILES,
+ RESTORED_AUTO_ADDED_TILES,
+ )
+
+ sendIntentForUser(autoAddIntent, user)
+
+ deviceProvisionedController.setUserSetup(user)
+
+ assertThat(restoreData).isNull()
+ }
+
+ @Test
+ fun restoreAfterUserSetup_otherUserFinishedSetup_noRestore() =
+ testScope.runTest {
+ runCurrent()
+ val restoreData by collectLastValue(underTest.restoreData)
+ val user = 0
+
+ val tilesIntent =
+ createRestoreIntent(
+ RestoreType.TILES,
+ CURRENT_TILES,
+ RESTORED_TILES,
+ )
+
+ sendIntentForUser(tilesIntent, user)
+
+ deviceProvisionedController.setUserSetup(user + 1)
+
+ assertThat(restoreData).isNull()
+ }
+
+ @Test
+ fun restoreAfterUserSetup_otherUserFinishedSetup_thenCorrectUser_restored() =
+ testScope.runTest {
+ runCurrent()
+ val restoreData by collectLastValue(underTest.restoreData)
+ val user = 0
+
+ val tilesIntent =
+ createRestoreIntent(
+ RestoreType.TILES,
+ CURRENT_TILES,
+ RESTORED_TILES,
+ )
+
+ sendIntentForUser(tilesIntent, user)
+
+ deviceProvisionedController.setUserSetup(user + 1)
+ runCurrent()
+ deviceProvisionedController.setUserSetup(user)
+
+ with(restoreData!!) {
+ assertThat(restoredTiles).isEqualTo(RESTORED_TILES.toTilesList())
+ assertThat(restoredAutoAddedTiles).isEmpty()
+ assertThat(userId).isEqualTo(user)
+ }
+ }
+
private fun sendIntentForUser(intent: Intent, userId: Int) {
fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
context,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
index 3418977c3211..37d472169ae5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
@@ -20,11 +20,11 @@ import android.platform.test.annotations.EnabledOnRavenwood
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.res.R
import com.android.systemui.retail.data.repository.FakeRetailModeRepository
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
@@ -187,6 +187,22 @@ class TileSpecSettingsRepositoryTest : SysuiTestCase() {
assertThat(loadTilesForUser(0)).isEqualTo(DEFAULT_TILES)
}
+ @Test
+ fun prependDefault() =
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tilesSpecs(0))
+
+ val startingTiles = listOf(TileSpec.create("e"), TileSpec.create("f"))
+
+ underTest.setTiles(0, startingTiles)
+ runCurrent()
+
+ underTest.prependDefault(0)
+
+ assertThat(tiles!!)
+ .containsExactlyElementsIn(DEFAULT_TILES.toTileSpecs() + startingTiles)
+ }
+
private fun TestScope.storeTilesForUser(specs: String, forUser: Int) {
secureSettings.putStringForUser(SETTING, specs, forUser)
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
index c7e7845f206c..bf34d6ee4435 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
import android.content.pm.UserInfo
+import android.content.pm.UserInfo.FLAG_DISABLED
import android.content.pm.UserInfo.FLAG_FULL
import android.content.pm.UserInfo.FLAG_MANAGED_PROFILE
import android.content.pm.UserInfo.FLAG_PRIMARY
@@ -73,14 +74,14 @@ class WorkTileAutoAddableTest : SysuiTestCase() {
fun changeInProfiles_hasManagedProfile_sendsAddSignal() = runTest {
val signal by collectLastValue(underTest.autoAddSignal(0))
- userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK_ENABLED), selectedUserIndex = 0)
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
}
@Test
fun changeInProfiles_noManagedProfile_sendsRemoveSignal() = runTest {
- userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK_ENABLED), selectedUserIndex = 0)
val signal by collectLastValue(underTest.autoAddSignal(0))
@@ -90,8 +91,17 @@ class WorkTileAutoAddableTest : SysuiTestCase() {
}
@Test
+ fun changeInProfile_hasDisabledManagedProfile_noAddSignal() = runTest {
+ val signal by collectLastValue(underTest.autoAddSignal(0))
+
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK_DISABLED), selectedUserIndex = 0)
+
+ assertThat(signal).isNotInstanceOf(AutoAddSignal.Add::class.java)
+ }
+
+ @Test
fun startingWithManagedProfile_sendsAddSignal() = runTest {
- userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK_ENABLED), selectedUserIndex = 0)
val signal by collectLastValue(underTest.autoAddSignal(0))
@@ -102,14 +112,14 @@ class WorkTileAutoAddableTest : SysuiTestCase() {
fun userChangeToUserWithProfile_noSignalForOriginalUser() = runTest {
val signal by collectLastValue(underTest.autoAddSignal(0))
- userTracker.set(listOf(USER_INFO_1, USER_INFO_WORK), selectedUserIndex = 0)
+ userTracker.set(listOf(USER_INFO_1, USER_INFO_WORK_ENABLED), selectedUserIndex = 0)
assertThat(signal).isNotEqualTo(AutoAddSignal.Add(SPEC))
}
@Test
fun userChangeToUserWithoutProfile_noSignalForOriginalUser() = runTest {
- userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK_ENABLED), selectedUserIndex = 0)
val signal by collectLastValue(underTest.autoAddSignal(0))
userTracker.set(listOf(USER_INFO_1), selectedUserIndex = 0)
@@ -137,7 +147,7 @@ class WorkTileAutoAddableTest : SysuiTestCase() {
@Test
fun restoreDataWithWorkTile_currentlyManagedProfile_doesntTriggerRemove() = runTest {
- userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK_ENABLED), selectedUserIndex = 0)
val userId = 0
val signals by collectValues(underTest.autoAddSignal(userId))
runCurrent()
@@ -164,7 +174,7 @@ class WorkTileAutoAddableTest : SysuiTestCase() {
@Test
fun restoreDataWithoutWorkTile_managedProfile_doesntTriggerRemove() = runTest {
- userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK_ENABLED), selectedUserIndex = 0)
val userId = 0
val signals by collectValues(underTest.autoAddSignal(userId))
runCurrent()
@@ -180,7 +190,9 @@ class WorkTileAutoAddableTest : SysuiTestCase() {
private val SPEC = TileSpec.create(WorkModeTile.TILE_SPEC)
private val USER_INFO_0 = UserInfo(0, "", FLAG_PRIMARY or FLAG_FULL)
private val USER_INFO_1 = UserInfo(1, "", FLAG_FULL)
- private val USER_INFO_WORK = UserInfo(10, "", FLAG_PROFILE or FLAG_MANAGED_PROFILE)
+ private val USER_INFO_WORK_DISABLED =
+ UserInfo(10, "", FLAG_PROFILE or FLAG_MANAGED_PROFILE or FLAG_DISABLED)
+ private val USER_INFO_WORK_ENABLED = UserInfo(10, "", FLAG_PROFILE or FLAG_MANAGED_PROFILE)
private fun createRestoreWithWorkTile(userId: Int): RestoreData {
return RestoreData(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index 1e2784a622b1..634c5fa74295 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -40,6 +40,7 @@ import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepositor
import com.android.systemui.qs.pipeline.data.repository.FakeCustomTileAddedRepository
import com.android.systemui.qs.pipeline.data.repository.FakeInstalledTilesComponentRepository
import com.android.systemui.qs.pipeline.data.repository.FakeTileSpecRepository
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesFixedRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
import com.android.systemui.qs.pipeline.domain.model.TileModel
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
@@ -82,6 +83,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() {
FakeCustomTileAddedRepository()
private val pipelineFlags = QSPipelineFlagsRepository()
private val tileLifecycleManagerFactory = TLMFactory()
+ private val minimumTilesRepository = MinimumTilesFixedRepository()
@Mock private lateinit var customTileStatePersister: CustomTileStatePersister
@@ -114,6 +116,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() {
tileSpecRepository = tileSpecRepository,
installedTilesComponentRepository = installedTilesPackageRepository,
userRepository = userRepository,
+ minimumTilesRepository = minimumTilesRepository,
customTileStatePersister = customTileStatePersister,
tileFactory = tileFactory,
newQSTileFactory = { newQSTileFactory },
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
new file mode 100644
index 000000000000..90c83047e72f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.pipeline.domain.interactor
+
+import android.content.ComponentName
+import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.FakeQSFactory
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.FakeDefaultTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesFixedRepository
+import com.android.systemui.qs.pipeline.data.repository.fakeDefaultTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.fakeMinimumTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.fakeRestoreRepository
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.qsTileFactory
+import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.settings.userTracker
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * This integration test is for testing the solution to b/324575996. In particular, when restoring
+ * from a device that uses different specs for tiles, we may end up with empty (or mostly empty) QS.
+ * In that case, we want to prepend the default tiles instead.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class NoLowNumberOfTilesTest : SysuiTestCase() {
+
+ private val USER_0_INFO =
+ UserInfo(
+ 0,
+ "zero",
+ "",
+ UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ )
+
+ private val defaultTiles =
+ listOf(
+ TileSpec.create("internet"),
+ TileSpec.create("bt"),
+ )
+
+ private val kosmos =
+ Kosmos().apply {
+ fakeMinimumTilesRepository = MinimumTilesFixedRepository(minNumberOfTiles = 2)
+ fakeUserTracker.set(listOf(USER_0_INFO), 0)
+ qsTileFactory = FakeQSFactory(::tileCreator)
+ fakeDefaultTilesRepository = FakeDefaultTilesRepository(defaultTiles)
+ }
+
+ private val currentUser: Int
+ get() = kosmos.userTracker.userId
+
+ private val goodTile = TileSpec.create("correct")
+
+ private val restoredTiles =
+ listOf(
+ TileSpec.create("OEM:internet"),
+ TileSpec.create("OEM:bt"),
+ TileSpec.create("OEM:dnd"),
+ // This is not an installed component so a tile won't be created
+ TileSpec.create(ComponentName.unflattenFromString("oem/.tile")!!),
+ TileSpec.create("OEM:flashlight"),
+ goodTile,
+ )
+
+ @Before
+ fun setUp() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_QS_NEW_PIPELINE)
+
+ with(kosmos) {
+ restoreReconciliationInteractor.start()
+ autoAddInteractor.init(kosmos.currentTilesInteractor)
+ }
+ }
+
+ @Test
+ fun noLessThanTwoTilesAfterOEMRestore_prependedDefault() =
+ with(kosmos) {
+ testScope.runTest {
+ val tiles by collectLastValue(currentTilesInteractor.currentTiles)
+ runCurrent()
+
+ assertThat(tiles!!).isNotEmpty()
+
+ val restoreData = RestoreData(restoredTiles, emptySet(), currentUser)
+ fakeRestoreRepository.onDataRestored(restoreData)
+ runCurrent()
+
+ assertThat(tiles!!.map { it.spec }).isEqualTo(defaultTiles + listOf(goodTile))
+ }
+ }
+
+ @Test
+ fun noEmptyTilesAfterSettingTilesToUnknownNames() =
+ with(kosmos) {
+ testScope.runTest {
+ val tiles by collectLastValue(currentTilesInteractor.currentTiles)
+ runCurrent()
+
+ assertThat(tiles!!).isNotEmpty()
+
+ val badTiles = listOf(TileSpec.create("OEM:unknown_tile"))
+ currentTilesInteractor.setTiles(badTiles)
+ runCurrent()
+
+ assertThat(tiles!!.map { it.spec }).isEqualTo(defaultTiles)
+ }
+ }
+
+ private fun tileCreator(spec: String): QSTile? {
+ return if (spec.contains("OEM")) {
+ null // We don't know how to create OEM spec tiles
+ } else {
+ FakeQSTile(currentUser)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index d47da3e47d2f..1eb9adb8c004 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -26,11 +26,12 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.FooterActionsController
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
-import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.UserAction
import com.android.systemui.scene.shared.model.UserActionResult
+import com.android.systemui.shade.domain.interactor.privacyChipInteractor
+import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
@@ -58,7 +59,6 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
private val qsFlexiglassAdapter = FakeQSSceneAdapter({ mock() })
@@ -95,9 +95,10 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
ShadeHeaderViewModel(
applicationScope = testScope.backgroundScope,
context = context,
- sceneInteractor = sceneInteractor,
mobileIconsInteractor = mobileIconsInteractor,
mobileIconsViewModel = mobileIconsViewModel,
+ privacyChipInteractor = kosmos.privacyChipInteractor,
+ clockInteractor = kosmos.shadeHeaderClockInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 9f89d346fd51..4e72843922e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -50,7 +50,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.model.SysUiState
import com.android.systemui.model.sceneContainerPlugin
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -65,8 +65,11 @@ import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.settings.FakeDisplayTracker
+import com.android.systemui.shade.domain.interactor.privacyChipInteractor
+import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
+import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
@@ -141,6 +144,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
SceneContainerViewModel(
sceneInteractor = sceneInteractor,
falsingInteractor = kosmos.falsingInteractor,
+ powerInteractor = kosmos.powerInteractor,
)
.apply { setTransitionState(transitionState) }
}
@@ -229,9 +233,10 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
ShadeHeaderViewModel(
applicationScope = testScope.backgroundScope,
context = context,
- sceneInteractor = sceneInteractor,
mobileIconsInteractor = mobileIconsInteractor,
mobileIconsViewModel = mobileIconsViewModel,
+ privacyChipInteractor = kosmos.privacyChipInteractor,
+ clockInteractor = kosmos.shadeHeaderClockInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
@@ -267,6 +272,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
windowController = mock(),
deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
centralSurfaces = mock(),
+ headsUpInteractor = kosmos.headsUpNotificationInteractor,
)
startable.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 4b9ebdc295c6..dd3eb6845789 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -22,7 +22,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.kosmos.testScope
-import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.sceneKeys
@@ -276,12 +275,4 @@ class SceneInteractorTest : SysuiTestCase() {
underTest.setVisible(true, "reason")
assertThat(isVisible).isTrue()
}
-
- @Test
- fun userInput() =
- testScope.runTest {
- assertThat(kosmos.fakePowerRepository.userTouchRegistered).isFalse()
- underTest.onUserInput()
- assertThat(kosmos.fakePowerRepository.userTouchRegistered).isTrue()
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index ffea84b70d07..f49b4777cf14 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -49,6 +49,8 @@ import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
+import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
@@ -120,6 +122,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
windowController = windowController,
deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
centralSurfaces = centralSurfaces,
+ headsUpInteractor = kosmos.headsUpNotificationInteractor,
)
}
@@ -168,6 +171,12 @@ class SceneContainerStartableTest : SysuiTestCase() {
fakeSceneDataSource.unpause(expectedScene = SceneKey.Gone)
transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
assertThat(isVisible).isFalse()
+
+ kosmos.headsUpNotificationRepository.hasPinnedHeadsUp.value = true
+ assertThat(isVisible).isTrue()
+
+ kosmos.headsUpNotificationRepository.hasPinnedHeadsUp.value = false
+ assertThat(isVisible).isFalse()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index 6c78317f61e5..ffbdafe338e7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -22,16 +22,23 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.domain.interactor.falsingInteractor
+import com.android.systemui.classifier.fakeFalsingManager
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.sceneKeys
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -45,6 +52,8 @@ class SceneContainerViewModelTest : SysuiTestCase() {
private val testScope by lazy { kosmos.testScope }
private val interactor by lazy { kosmos.sceneInteractor }
private val fakeSceneDataSource = kosmos.fakeSceneDataSource
+ private val sceneContainerConfig = kosmos.sceneContainerConfig
+ private val falsingManager = kosmos.fakeFalsingManager
private lateinit var underTest: SceneContainerViewModel
@@ -55,6 +64,7 @@ class SceneContainerViewModelTest : SysuiTestCase() {
SceneContainerViewModel(
sceneInteractor = interactor,
falsingInteractor = kosmos.falsingInteractor,
+ powerInteractor = kosmos.powerInteractor,
)
}
@@ -86,4 +96,107 @@ class SceneContainerViewModelTest : SysuiTestCase() {
assertThat(currentScene).isEqualTo(SceneKey.Shade)
}
+
+ @Test
+ fun canChangeScene_whenAllowed_switchingFromGone_returnsTrue() =
+ testScope.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ fakeSceneDataSource.changeScene(toScene = SceneKey.Gone)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Gone)
+
+ sceneContainerConfig.sceneKeys
+ .filter { it != currentScene }
+ .forEach { toScene ->
+ assertWithMessage("Scene $toScene incorrectly protected when allowed")
+ .that(underTest.canChangeScene(toScene = toScene))
+ .isTrue()
+ }
+ }
+
+ @Test
+ fun canChangeScene_whenAllowed_switchingFromLockscreen_returnsTrue() =
+ testScope.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+
+ sceneContainerConfig.sceneKeys
+ .filter { it != currentScene }
+ .forEach { toScene ->
+ assertWithMessage("Scene $toScene incorrectly protected when allowed")
+ .that(underTest.canChangeScene(toScene = toScene))
+ .isTrue()
+ }
+ }
+
+ @Test
+ fun canChangeScene_whenNotAllowed_fromLockscreen_toFalsingProtectedScenes_returnsFalse() =
+ testScope.runTest {
+ falsingManager.setIsFalseTouch(true)
+ val currentScene by collectLastValue(underTest.currentScene)
+ fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+
+ sceneContainerConfig.sceneKeys
+ .filter { it != currentScene }
+ .filter {
+ // Moving to the Communal scene is not currently falsing protected.
+ it != SceneKey.Communal
+ }
+ .forEach { toScene ->
+ assertWithMessage("Protected scene $toScene not properly protected")
+ .that(underTest.canChangeScene(toScene = toScene))
+ .isFalse()
+ }
+ }
+
+ @Test
+ fun canChangeScene_whenNotAllowed_fromLockscreen_toFalsingUnprotectedScenes_returnsTrue() =
+ testScope.runTest {
+ falsingManager.setIsFalseTouch(true)
+ val currentScene by collectLastValue(underTest.currentScene)
+ fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+
+ sceneContainerConfig.sceneKeys
+ .filter {
+ // Moving to the Communal scene is not currently falsing protected.
+ it == SceneKey.Communal
+ }
+ .forEach { toScene ->
+ assertWithMessage("Unprotected scene $toScene is incorrectly protected")
+ .that(underTest.canChangeScene(toScene = toScene))
+ .isTrue()
+ }
+ }
+
+ @Test
+ fun canChangeScene_whenNotAllowed_fromGone_toAnyOtherScene_returnsTrue() =
+ testScope.runTest {
+ falsingManager.setIsFalseTouch(true)
+ val currentScene by collectLastValue(underTest.currentScene)
+ fakeSceneDataSource.changeScene(toScene = SceneKey.Gone)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Gone)
+
+ sceneContainerConfig.sceneKeys
+ .filter { it != currentScene }
+ .forEach { toScene ->
+ assertWithMessage("Protected scene $toScene not properly protected")
+ .that(underTest.canChangeScene(toScene = toScene))
+ .isTrue()
+ }
+ }
+
+ @Test
+ fun userInput() =
+ testScope.runTest {
+ assertThat(kosmos.fakePowerRepository.userTouchRegistered).isFalse()
+ underTest.onMotionEvent(mock())
+ assertThat(kosmos.fakePowerRepository.userTouchRegistered).isTrue()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryTest.kt
new file mode 100644
index 000000000000..613f256113f3
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryTest.kt
@@ -0,0 +1,183 @@
+/*
+ * 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.shade.data.repository
+
+import android.content.Intent
+import android.safetycenter.SafetyCenterManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.privacy.PrivacyApplication
+import com.android.systemui.privacy.PrivacyConfig
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.privacy.PrivacyItemController
+import com.android.systemui.privacy.PrivacyType
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.kotlinArgumentCaptor
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+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.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrivacyChipRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val broadcastDispatcher = kosmos.broadcastDispatcher
+
+ @Mock private lateinit var privacyConfig: PrivacyConfig
+ @Mock private lateinit var privacyItemController: PrivacyItemController
+ @Mock private lateinit var safetyCenterManager: SafetyCenterManager
+
+ lateinit var underTest: PrivacyChipRepositoryImpl
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ setUpUnderTest()
+ }
+
+ @Test
+ fun isSafetyCenterEnabled_startEnabled() =
+ testScope.runTest {
+ setUpUnderTest(true)
+
+ val actual by collectLastValue(underTest.isSafetyCenterEnabled)
+ runCurrent()
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isSafetyCenterEnabled_startDisabled() =
+ testScope.runTest {
+ setUpUnderTest(false)
+
+ val actual by collectLastValue(underTest.isSafetyCenterEnabled)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isSafetyCenterEnabled_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isSafetyCenterEnabled)
+ runCurrent()
+
+ assertThat(actual).isFalse()
+
+ whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true)
+
+ broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED),
+ )
+
+ runCurrent()
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun privacyItems_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.privacyItems)
+ runCurrent()
+
+ val callback =
+ withArgCaptor<PrivacyItemController.Callback> {
+ verify(privacyItemController).addCallback(capture())
+ }
+
+ callback.onPrivacyItemsChanged(emptyList())
+ assertThat(actual).isEmpty()
+
+ val privacyItems =
+ listOf(
+ PrivacyItem(
+ privacyType = PrivacyType.TYPE_CAMERA,
+ application = PrivacyApplication("", 0)
+ ),
+ )
+ callback.onPrivacyItemsChanged(privacyItems)
+ assertThat(actual).isEqualTo(privacyItems)
+ }
+
+ @Test
+ fun isMicCameraIndicationEnabled_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isMicCameraIndicationEnabled)
+ runCurrent()
+
+ val captor = kotlinArgumentCaptor<PrivacyConfig.Callback>()
+ verify(privacyConfig, times(2)).addCallback(captor.capture())
+ val callback = captor.allValues[0]
+
+ callback.onFlagMicCameraChanged(false)
+ assertThat(actual).isFalse()
+
+ callback.onFlagMicCameraChanged(true)
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isLocationIndicationEnabled_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isLocationIndicationEnabled)
+ runCurrent()
+
+ val captor = kotlinArgumentCaptor<PrivacyConfig.Callback>()
+ verify(privacyConfig, times(2)).addCallback(captor.capture())
+ val callback = captor.allValues[1]
+
+ callback.onFlagLocationChanged(false)
+ assertThat(actual).isFalse()
+
+ callback.onFlagLocationChanged(true)
+ assertThat(actual).isTrue()
+ }
+
+ private fun setUpUnderTest(isSafetyCenterEnabled: Boolean = false) {
+ whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(isSafetyCenterEnabled)
+
+ underTest =
+ PrivacyChipRepositoryImpl(
+ applicationScope = kosmos.applicationCoroutineScope,
+ privacyConfig = privacyConfig,
+ privacyItemController = privacyItemController,
+ backgroundDispatcher = kosmos.testDispatcher,
+ broadcastDispatcher = broadcastDispatcher,
+ safetyCenterManager = safetyCenterManager,
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryTest.kt
new file mode 100644
index 000000000000..d7b77e613c2d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryTest.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.shade.data.repository
+
+import android.app.AlarmManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback
+import com.android.systemui.statusbar.policy.nextAlarmController
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShadeHeaderClockRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val nextAlarmController = kosmos.nextAlarmController
+
+ val underTest = kosmos.shadeHeaderClockRepository
+
+ @Test
+ fun nextAlarmIntent_updates() =
+ testScope.runTest {
+ assertThat(underTest.nextAlarmIntent).isNull()
+
+ val callback =
+ withArgCaptor<NextAlarmChangeCallback> {
+ verify(nextAlarmController).addCallback(capture())
+ }
+
+ callback.onNextAlarmChanged(AlarmManager.AlarmClockInfo(1L, mock()))
+ assertThat(underTest.nextAlarmIntent).isNotNull()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorTest.kt
new file mode 100644
index 000000000000..f0293a8efc8a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorTest.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.shade.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyApplication
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.privacy.PrivacyType
+import com.android.systemui.privacy.privacyDialogController
+import com.android.systemui.privacy.privacyDialogControllerV2
+import com.android.systemui.shade.data.repository.fakePrivacyChipRepository
+import com.android.systemui.shade.data.repository.privacyChipRepository
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+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.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrivacyChipInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val privacyChipRepository = kosmos.fakePrivacyChipRepository
+ private val privacyDialogController = kosmos.privacyDialogController
+ private val privacyDialogControllerV2 = kosmos.privacyDialogControllerV2
+ @Mock private lateinit var privacyChip: OngoingPrivacyChip
+
+ val underTest = kosmos.privacyChipInteractor
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ whenever(privacyChip.context).thenReturn(this.context)
+ }
+
+ @Test
+ fun isChipVisible_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipVisible)
+
+ privacyChipRepository.setPrivacyItems(emptyList())
+ runCurrent()
+
+ assertThat(actual).isFalse()
+
+ val privacyItems =
+ listOf(
+ PrivacyItem(
+ privacyType = PrivacyType.TYPE_CAMERA,
+ application = PrivacyApplication("", 0)
+ ),
+ )
+ privacyChipRepository.setPrivacyItems(privacyItems)
+ runCurrent()
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isChipEnabled_noIndicationEnabled() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipEnabled)
+
+ privacyChipRepository.setIsMicCameraIndicationEnabled(false)
+ privacyChipRepository.setIsLocationIndicationEnabled(false)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isChipEnabled_micCameraIndicationEnabled() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipEnabled)
+
+ privacyChipRepository.setIsMicCameraIndicationEnabled(true)
+ privacyChipRepository.setIsLocationIndicationEnabled(false)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isChipEnabled_locationIndicationEnabled() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipEnabled)
+
+ privacyChipRepository.setIsMicCameraIndicationEnabled(false)
+ privacyChipRepository.setIsLocationIndicationEnabled(true)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isChipEnabled_allIndicationEnabled() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipEnabled)
+
+ privacyChipRepository.setIsMicCameraIndicationEnabled(true)
+ privacyChipRepository.setIsLocationIndicationEnabled(true)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun onPrivacyChipClicked_safetyCenterEnabled() =
+ testScope.runTest {
+ privacyChipRepository.setIsSafetyCenterEnabled(true)
+
+ underTest.onPrivacyChipClicked(privacyChip)
+
+ verify(privacyDialogControllerV2).showDialog(any(), any())
+ verify(privacyDialogController, never()).showDialog(any())
+ }
+
+ @Test
+ fun onPrivacyChipClicked_safetyCenterDisabled() =
+ testScope.runTest {
+ privacyChipRepository.setIsSafetyCenterEnabled(false)
+
+ underTest.onPrivacyChipClicked(privacyChip)
+
+ verify(privacyDialogController).showDialog(any())
+ verify(privacyDialogControllerV2, never()).showDialog(any(), any())
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorTest.kt
new file mode 100644
index 000000000000..84fc93008f49
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorTest.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.shade.domain.interactor
+
+import android.app.AlarmManager
+import android.content.Intent
+import android.provider.AlarmClock
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback
+import com.android.systemui.statusbar.policy.nextAlarmController
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argThat
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.withArgCaptor
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatcher
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShadeHeaderClockInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val activityStarter = kosmos.activityStarter
+ private val nextAlarmController = kosmos.nextAlarmController
+
+ val underTest = kosmos.shadeHeaderClockInteractor
+
+ @Test
+ fun launchClockActivity_default() =
+ testScope.runTest {
+ underTest.launchClockActivity()
+ verify(activityStarter)
+ .postStartActivityDismissingKeyguard(
+ argThat(IntentMatcherAction(AlarmClock.ACTION_SHOW_ALARMS)),
+ any()
+ )
+ }
+
+ @Test
+ fun launchClockActivity_nextAlarmIntent() =
+ testScope.runTest {
+ val callback =
+ withArgCaptor<NextAlarmChangeCallback> {
+ verify(nextAlarmController).addCallback(capture())
+ }
+ callback.onNextAlarmChanged(AlarmManager.AlarmClockInfo(1L, mock()))
+
+ underTest.launchClockActivity()
+ verify(activityStarter).postStartActivityDismissingKeyguard(any())
+ }
+}
+
+private class IntentMatcherAction(private val action: String) : ArgumentMatcher<Intent> {
+ override fun matches(argument: Intent?): Boolean {
+ return argument?.action == action
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index c0aaab3ad6e1..062741df01cf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -1,5 +1,7 @@
package com.android.systemui.shade.ui.viewmodel
+import android.content.Intent
+import android.provider.AlarmClock
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -8,7 +10,9 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.shade.domain.interactor.privacyChipInteractor
+import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
@@ -18,12 +22,16 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsVi
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.argThat
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatcher
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@@ -31,7 +39,6 @@ import org.mockito.MockitoAnnotations
class ShadeHeaderViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
@@ -62,9 +69,10 @@ class ShadeHeaderViewModelTest : SysuiTestCase() {
ShadeHeaderViewModel(
applicationScope = testScope.backgroundScope,
context = context,
- sceneInteractor = sceneInteractor,
mobileIconsInteractor = mobileIconsInteractor,
mobileIconsViewModel = mobileIconsViewModel,
+ privacyChipInteractor = kosmos.privacyChipInteractor,
+ clockInteractor = kosmos.shadeHeaderClockInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
}
@@ -82,6 +90,19 @@ class ShadeHeaderViewModelTest : SysuiTestCase() {
assertThat(mobileSubIds).isEqualTo(listOf(1, 2))
}
+ @Test
+ fun onClockClicked_launchesClock() =
+ testScope.runTest {
+ val activityStarter = kosmos.activityStarter
+ underTest.onClockClicked()
+
+ verify(activityStarter)
+ .postStartActivityDismissingKeyguard(
+ argThat(IntentMatcherAction(AlarmClock.ACTION_SHOW_ALARMS)),
+ anyInt(),
+ )
+ }
+
companion object {
private val SUB_1 =
SubscriptionModel(
@@ -99,3 +120,9 @@ class ShadeHeaderViewModelTest : SysuiTestCase() {
)
}
}
+
+private class IntentMatcherAction(private val action: String) : ArgumentMatcher<Intent> {
+ override fun matches(argument: Intent?): Boolean {
+ return argument?.action == action
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 799e8f054d51..d655ade5cf2c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -27,10 +27,12 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.shade.domain.interactor.privacyChipInteractor
+import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
@@ -96,9 +98,10 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
ShadeHeaderViewModel(
applicationScope = testScope.backgroundScope,
context = context,
- sceneInteractor = sceneInteractor,
mobileIconsInteractor = mobileIconsInteractor,
mobileIconsViewModel = mobileIconsViewModel,
+ privacyChipInteractor = kosmos.privacyChipInteractor,
+ clockInteractor = kosmos.shadeHeaderClockInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
@@ -166,6 +169,32 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
}
@Test
+ fun isClickable_deviceUnlocked_false() =
+ testScope.runTest {
+ val isClickable by collectLastValue(underTest.isClickable)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+ kosmos.fakeDeviceEntryRepository.setUnlocked(true)
+ runCurrent()
+
+ assertThat(isClickable).isFalse()
+ }
+
+ @Test
+ fun isClickable_deviceLockedSecurely_true() =
+ testScope.runTest {
+ val isClickable by collectLastValue(underTest.isClickable)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+ kosmos.fakeDeviceEntryRepository.setUnlocked(false)
+ runCurrent()
+
+ assertThat(isClickable).isTrue()
+ }
+
+ @Test
fun onContentClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt
new file mode 100644
index 000000000000..e188f5bfc1c8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.domain.interactor
+
+import android.app.NotificationManager
+import android.media.AudioManager
+import android.provider.Settings.Global
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.statusbar.notification.data.model.ZenMode
+import com.android.settingslib.statusbar.notification.data.repository.updateNotificationPolicy
+import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Expect
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationsSoundPolicyInteractorTest : SysuiTestCase() {
+
+ @JvmField @Rule val expect = Expect.create()
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: NotificationsSoundPolicyInteractor
+
+ @Before
+ fun setup() {
+ with(kosmos) {
+ underTest = NotificationsSoundPolicyInteractor(notificationsSoundPolicyRepository)
+ }
+ }
+
+ @Test
+ fun onlyAlarmsCategory_areAlarmsAllowed_isTrue() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateZenMode(ZenMode(Global.ZEN_MODE_OFF))
+ val expectedByCategory =
+ NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.associateWith {
+ it == NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
+ }
+ expectedByCategory.forEach { entry ->
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories = entry.key
+ )
+
+ val areAlarmsAllowed by collectLastValue(underTest.areAlarmsAllowed)
+ runCurrent()
+
+ expect.that(areAlarmsAllowed).isEqualTo(entry.value)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun onlyMediaCategory_areAlarmsAllowed_isTrue() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateZenMode(ZenMode(Global.ZEN_MODE_OFF))
+ val expectedByCategory =
+ NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.associateWith {
+ it == NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA
+ }
+ expectedByCategory.forEach { entry ->
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories = entry.key
+ )
+
+ val isMediaAllowed by collectLastValue(underTest.isMediaAllowed)
+ runCurrent()
+
+ expect.that(isMediaAllowed).isEqualTo(entry.value)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun atLeastOneCategoryAllowed_isRingerAllowed_isTrue() {
+ with(kosmos) {
+ testScope.runTest {
+ for (category in NotificationManager.Policy.ALL_PRIORITY_CATEGORIES) {
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories = category,
+ state = NotificationManager.Policy.STATE_UNSET,
+ )
+
+ val isRingerAllowed by collectLastValue(underTest.isRingerAllowed)
+ runCurrent()
+
+ expect.that(isRingerAllowed).isTrue()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun allCategoriesAllowed_isRingerAllowed_isTrue() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories =
+ NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.reduce { acc, value ->
+ acc or value
+ },
+ state = NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED,
+ )
+
+ val isRingerAllowed by collectLastValue(underTest.isRingerAllowed)
+ runCurrent()
+
+ assertThat(isRingerAllowed).isTrue()
+ }
+ }
+ }
+
+ @Test
+ fun noCategoriesAndBlocked_isRingerAllowed_isFalse() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories = 0,
+ state = NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED,
+ )
+
+ val isRingerAllowed by collectLastValue(underTest.isRingerAllowed)
+ runCurrent()
+
+ assertThat(isRingerAllowed).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun zenModeNoInterruptions_allStreams_muted() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateNotificationPolicy()
+ notificationsSoundPolicyRepository.updateZenMode(
+ ZenMode(Global.ZEN_MODE_NO_INTERRUPTIONS)
+ )
+
+ for (stream in AudioStream.supportedStreamTypes) {
+ val isZenMuted by collectLastValue(underTest.isZenMuted(AudioStream(stream)))
+ runCurrent()
+
+ expect.that(isZenMuted).isTrue()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun zenModeOff_allStreams_notMuted() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateNotificationPolicy()
+ notificationsSoundPolicyRepository.updateZenMode(ZenMode(Global.ZEN_MODE_OFF))
+
+ for (stream in AudioStream.supportedStreamTypes) {
+ val isZenMuted by collectLastValue(underTest.isZenMuted(AudioStream(stream)))
+ runCurrent()
+
+ expect.that(isZenMuted).isFalse()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun zenModeAlarms_ringAndNotifications_muted() {
+ with(kosmos) {
+ val expectedToBeMuted =
+ setOf(AudioManager.STREAM_RING, AudioManager.STREAM_NOTIFICATION)
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateNotificationPolicy()
+ notificationsSoundPolicyRepository.updateZenMode(ZenMode(Global.ZEN_MODE_ALARMS))
+
+ for (stream in AudioStream.supportedStreamTypes) {
+ val isZenMuted by collectLastValue(underTest.isZenMuted(AudioStream(stream)))
+ runCurrent()
+
+ expect.that(isZenMuted).isEqualTo(stream in expectedToBeMuted)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun alarms_allowed_notMuted() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateZenMode(
+ ZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ )
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories = NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
+ )
+
+ val isZenMuted by
+ collectLastValue(underTest.isZenMuted(AudioStream(AudioManager.STREAM_ALARM)))
+ runCurrent()
+
+ expect.that(isZenMuted).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun media_allowed_notMuted() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateZenMode(
+ ZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ )
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories = NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA
+ )
+
+ val isZenMuted by
+ collectLastValue(underTest.isZenMuted(AudioStream(AudioManager.STREAM_MUSIC)))
+ runCurrent()
+
+ expect.that(isZenMuted).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun ringer_allowed_notificationsNotMuted() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateZenMode(
+ ZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ )
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories =
+ NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.reduce { acc, value ->
+ acc or value
+ },
+ state = NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED,
+ )
+
+ val isZenMuted by
+ collectLastValue(
+ underTest.isZenMuted(AudioStream(AudioManager.STREAM_NOTIFICATION))
+ )
+ runCurrent()
+
+ expect.that(isZenMuted).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun ringer_allowed_ringNotMuted() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateZenMode(
+ ZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ )
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories =
+ NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.reduce { acc, value ->
+ acc or value
+ },
+ state = NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED,
+ )
+
+ val isZenMuted by
+ collectLastValue(underTest.isZenMuted(AudioStream(AudioManager.STREAM_RING)))
+ runCurrent()
+
+ expect.that(isZenMuted).isFalse()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 3e0082c5aed7..0641c610aaff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -681,7 +681,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun shadeCollapseFadeIn() =
testScope.runTest {
- val fadeIn by collectLastValue(underTest.shadeCollpaseFadeIn)
+ val fadeIn by collectLastValue(underTest.shadeCollapseFadeIn)
// Start on lockscreen without the shade
underTest.setShadeCollapseFadeInComplete(false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index aa6b4ac4970f..c804fc6990ae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -1138,7 +1138,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
private fun setCameraProtectionBounds(protectionBounds: Rect) {
val protectionInfo =
- mock<CameraProtectionInfo> { whenever(this.cutoutBounds).thenReturn(protectionBounds) }
+ mock<CameraProtectionInfo> { whenever(this.bounds).thenReturn(protectionBounds) }
whenever(sysUICutout.cameraProtection).thenReturn(protectionInfo)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/OWNERS b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/OWNERS
new file mode 100644
index 000000000000..1f07df9f1e8e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/OWNERS
@@ -0,0 +1 @@
+include /packages/SystemUI/src/com/android/systemui/volume/OWNERS \ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
new file mode 100644
index 000000000000..a2f3ccb8c416
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.domain.interactor
+
+import android.media.AudioManager
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.statusbar.notification.data.model.ZenMode
+import com.android.settingslib.statusbar.notification.data.repository.updateNotificationPolicy
+import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.settingslib.volume.shared.model.RingerMode
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.notification.domain.interactor.notificationsSoundPolicyInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.notificationsSoundPolicyRepository
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.audioRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class AudioVolumeInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: AudioVolumeInteractor
+
+ @Before
+ fun setup() {
+ with(kosmos) {
+ underTest = AudioVolumeInteractor(audioRepository, notificationsSoundPolicyInteractor)
+
+ audioRepository.setRingerMode(RingerMode(AudioManager.RINGER_MODE_NORMAL))
+
+ notificationsSoundPolicyRepository.updateNotificationPolicy()
+ notificationsSoundPolicyRepository.updateZenMode(ZenMode(Settings.Global.ZEN_MODE_OFF))
+ }
+ }
+
+ @Test
+ fun setMuted_mutesStream() {
+ with(kosmos) {
+ testScope.runTest {
+ val model by collectLastValue(underTest.getAudioStream(audioStream))
+
+ underTest.setMuted(audioStream, false)
+ runCurrent()
+ assertThat(model!!.isMuted).isFalse()
+
+ underTest.setMuted(audioStream, true)
+ runCurrent()
+ assertThat(model!!.isMuted).isTrue()
+ }
+ }
+ }
+
+ @Test
+ fun setVolume_changesVolume() {
+ with(kosmos) {
+ testScope.runTest {
+ val model by collectLastValue(underTest.getAudioStream(audioStream))
+
+ underTest.setVolume(audioStream, 10)
+ runCurrent()
+ assertThat(model!!.volume).isEqualTo(10)
+
+ underTest.setVolume(audioStream, 20)
+ runCurrent()
+ assertThat(model!!.volume).isEqualTo(20)
+ }
+ }
+ }
+
+ @Test
+ fun ringMuted_notificationVolume_cantChange() {
+ with(kosmos) {
+ testScope.runTest {
+ val canChangeVolume by
+ collectLastValue(
+ underTest.canChangeVolume(AudioStream(AudioManager.STREAM_NOTIFICATION))
+ )
+
+ underTest.setMuted(AudioStream(AudioManager.STREAM_RING), true)
+ runCurrent()
+
+ assertThat(canChangeVolume).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun streamIsMuted_getStream_volumeZero() {
+ with(kosmos) {
+ testScope.runTest {
+ val model by collectLastValue(underTest.getAudioStream(audioStream))
+
+ underTest.setMuted(audioStream, true)
+ runCurrent()
+
+ assertThat(model!!.volume).isEqualTo(0)
+ }
+ }
+ }
+
+ @Test
+ fun streamIsZenMuted_getStream_lastAudibleVolume() {
+ with(kosmos) {
+ testScope.runTest {
+ audioRepository.setLastAudibleVolume(audioStream, 30)
+ notificationsSoundPolicyRepository.updateZenMode(
+ ZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
+ )
+
+ val model by collectLastValue(underTest.getAudioStream(audioStream))
+ runCurrent()
+
+ assertThat(model!!.volume).isEqualTo(30)
+ }
+ }
+ }
+
+ @Test
+ fun ringerModeVibrateAndMuted_getNotificationStream_volumeIsZero() {
+ with(kosmos) {
+ testScope.runTest {
+ audioRepository.setRingerMode(RingerMode(AudioManager.RINGER_MODE_VIBRATE))
+ underTest.setMuted(AudioStream(AudioManager.STREAM_NOTIFICATION), true)
+
+ val model by
+ collectLastValue(
+ underTest.getAudioStream(AudioStream(AudioManager.STREAM_NOTIFICATION))
+ )
+ runCurrent()
+
+ assertThat(model!!.volume).isEqualTo(0)
+ }
+ }
+ }
+
+ @Test
+ fun ringerModeVibrate_getRingerStream_volumeIsZero() {
+ with(kosmos) {
+ testScope.runTest {
+ audioRepository.setRingerMode(RingerMode(AudioManager.RINGER_MODE_VIBRATE))
+
+ val model by
+ collectLastValue(
+ underTest.getAudioStream(AudioStream(AudioManager.STREAM_RING))
+ )
+ runCurrent()
+
+ assertThat(model!!.volume).isEqualTo(0)
+ }
+ }
+ }
+
+ private companion object {
+ val audioStream = AudioStream(AudioManager.STREAM_SYSTEM)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
new file mode 100644
index 000000000000..e31cdcd82e7e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc.data.repository
+
+import android.bluetooth.BluetoothDevice
+import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.media.BluetoothMediaDevice
+import com.android.settingslib.media.MediaDevice
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.volume.localMediaRepository
+import com.android.systemui.volume.localMediaRepositoryFactory
+import com.android.systemui.volume.panel.component.anc.FakeSliceFactory
+import com.android.systemui.volume.panel.component.anc.sliceViewManager
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AncSliceRepositoryTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: AncSliceRepository
+
+ @Before
+ fun setup() {
+ with(kosmos) {
+ val slice = FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
+ whenever(sliceViewManager.bindSlice(any<Uri>())).thenReturn(slice)
+
+ underTest =
+ AncSliceRepositoryImpl(
+ localMediaRepositoryFactory,
+ testScope.testScheduler,
+ sliceViewManager,
+ )
+ }
+ }
+
+ @Test
+ fun noConnectedDevice_noSlice() {
+ with(kosmos) {
+ testScope.runTest {
+ localMediaRepository.updateCurrentConnectedDevice(null)
+
+ val slice by collectLastValue(underTest.ancSlice(1))
+ runCurrent()
+
+ assertThat(slice).isNull()
+ }
+ }
+ }
+
+ @Test
+ fun connectedDevice_sliceReturned() {
+ with(kosmos) {
+ testScope.runTest {
+ localMediaRepository.updateCurrentConnectedDevice(createMediaDevice())
+
+ val slice by collectLastValue(underTest.ancSlice(1))
+ runCurrent()
+
+ assertThat(slice).isNotNull()
+ }
+ }
+ }
+
+ private fun createMediaDevice(sliceUri: String = "content://test.slice"): MediaDevice {
+ val bluetoothDevice: BluetoothDevice = mock {
+ whenever(getMetadata(any()))
+ .thenReturn(
+ ("<HEARABLE_CONTROL_SLICE_WITH_WIDTH>" +
+ sliceUri +
+ "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>")
+ .toByteArray()
+ )
+ }
+ val cachedBluetoothDevice: CachedBluetoothDevice = mock {
+ whenever(device).thenReturn(bluetoothDevice)
+ }
+ return mock<BluetoothMediaDevice> {
+ whenever(cachedDevice).thenReturn(cachedBluetoothDevice)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt
new file mode 100644
index 000000000000..553aed8cfb05
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc.domain
+
+import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.volume.panel.component.anc.FakeSliceFactory
+import com.android.systemui.volume.panel.component.anc.ancSliceInteractor
+import com.android.systemui.volume.panel.component.anc.ancSliceRepository
+import com.android.systemui.volume.panel.component.anc.sliceViewManager
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AncAvailabilityCriteriaTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: AncAvailabilityCriteria
+
+ @Before
+ fun setup() {
+ with(kosmos) {
+ whenever(sliceViewManager.bindSlice(any<Uri>())).thenReturn(mock {})
+
+ underTest = AncAvailabilityCriteria(ancSliceInteractor)
+ }
+ }
+
+ @Test
+ fun noSlice_unavailable() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(1, null)
+
+ val isAvailable by collectLastValue(underTest.isAvailable())
+ runCurrent()
+
+ assertThat(isAvailable).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun hasSlice_available() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(
+ 1,
+ FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
+ )
+
+ val isAvailable by collectLastValue(underTest.isAvailable())
+ runCurrent()
+
+ assertThat(isAvailable).isTrue()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
new file mode 100644
index 000000000000..53f0bc9ddb51
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.panel.component.anc.FakeSliceFactory
+import com.android.systemui.volume.panel.component.anc.ancSliceRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AncSliceInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: AncSliceInteractor
+
+ @Before
+ fun setup() {
+ with(kosmos) {
+ underTest = AncSliceInteractor(ancSliceRepository, testScope.backgroundScope)
+ }
+ }
+
+ @Test
+ fun errorSlice_returnsNull() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(
+ 1,
+ FakeSliceFactory.createSlice(hasError = true, hasSliceItem = true)
+ )
+
+ val slice by collectLastValue(underTest.ancSlice)
+ runCurrent()
+
+ assertThat(slice).isNull()
+ }
+ }
+ }
+
+ @Test
+ fun noSliceItem_returnsNull() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(
+ 1,
+ FakeSliceFactory.createSlice(hasError = false, hasSliceItem = false)
+ )
+
+ val slice by collectLastValue(underTest.ancSlice)
+ runCurrent()
+
+ assertThat(slice).isNull()
+ }
+ }
+ }
+
+ @Test
+ fun sliceItem_noError_returnsSlice() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(
+ 1,
+ FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
+ )
+
+ val slice by collectLastValue(underTest.ancSlice)
+ runCurrent()
+
+ assertThat(slice).isNotNull()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt
index ec37925af0f3..ec55c75d4ae5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt
@@ -17,8 +17,6 @@
package com.android.systemui.volume.panel.component.mediaoutput.domain
import android.media.AudioManager
-import android.media.session.MediaSession
-import android.media.session.PlaybackState
import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -26,14 +24,8 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.volume.audioModeInteractor
import com.android.systemui.volume.audioRepository
-import com.android.systemui.volume.localMediaRepository
-import com.android.systemui.volume.mediaController
-import com.android.systemui.volume.mediaControllerRepository
-import com.android.systemui.volume.mediaOutputInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -54,23 +46,14 @@ class MediaOutputAvailabilityCriteriaTest : SysuiTestCase() {
@Before
fun setup() {
- with(kosmos) {
- whenever(mediaController.packageName).thenReturn("test.pkg")
- whenever(mediaController.sessionToken).thenReturn(MediaSession.Token(0, mock {}))
- whenever(mediaController.playbackState).thenReturn(PlaybackState.Builder().build())
-
- mediaControllerRepository.setActiveLocalMediaController(mediaController)
-
- underTest = MediaOutputAvailabilityCriteria(mediaOutputInteractor, audioModeInteractor)
- }
+ underTest = MediaOutputAvailabilityCriteria(kosmos.audioModeInteractor)
}
@Test
- fun notInCallAndHasDevices_isAvailable_true() {
+ fun notInCall_isAvailable_true() {
with(kosmos) {
testScope.runTest {
audioRepository.setMode(AudioManager.MODE_NORMAL)
- localMediaRepository.updateMediaDevices(listOf(mock {}))
val isAvailable by collectLastValue(underTest.isAvailable())
runCurrent()
@@ -79,27 +62,12 @@ class MediaOutputAvailabilityCriteriaTest : SysuiTestCase() {
}
}
}
- @Test
- fun inCallAndHasDevices_isAvailable_false() {
- with(kosmos) {
- testScope.runTest {
- audioRepository.setMode(AudioManager.MODE_IN_CALL)
- localMediaRepository.updateMediaDevices(listOf(mock {}))
-
- val isAvailable by collectLastValue(underTest.isAvailable())
- runCurrent()
-
- assertThat(isAvailable).isFalse()
- }
- }
- }
@Test
- fun notInCallAndHasDevices_isAvailable_false() {
+ fun inCall_isAvailable_false() {
with(kosmos) {
testScope.runTest {
- audioRepository.setMode(AudioManager.MODE_NORMAL)
- localMediaRepository.updateMediaDevices(emptyList())
+ audioRepository.setMode(AudioManager.MODE_IN_CALL)
val isAvailable by collectLastValue(underTest.isAvailable())
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt
new file mode 100644
index 000000000000..a1e4fcafd3a4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.volume.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+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
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class VolumeSliderInteractorTest : SysuiTestCase() {
+
+ private val underTest = VolumeSliderInteractor()
+
+ @Test
+ fun translateValueToVolume() {
+ assertThat(underTest.translateValueToVolume(30f, volumeRange)).isEqualTo(3)
+ }
+
+ @Test
+ fun processVolumeToValue_muted_zero() {
+ assertThat(underTest.processVolumeToValue(3, volumeRange, null, true)).isEqualTo(0)
+ }
+
+ @Test
+ fun processVolumeToValue_currentValue_currentValue() {
+ assertThat(underTest.processVolumeToValue(3, volumeRange, 30f, false)).isEqualTo(30f)
+ }
+
+ @Test
+ fun processVolumeToValue_currentValueDiffersVolume_returnsTranslatedVolume() {
+ assertThat(underTest.processVolumeToValue(1, volumeRange, 60f, false)).isEqualTo(10f)
+ }
+
+ @Test
+ fun processVolumeToValue_currentValueDiffersNotEnoughVolume_returnsTranslatedVolume() {
+ assertThat(underTest.processVolumeToValue(1, volumeRange, 12f, false)).isEqualTo(12f)
+ }
+
+ private companion object {
+ val volumeRange = 0..10
+ }
+}
diff --git a/packages/SystemUI/res/drawable/ic_noise_aware.xml b/packages/SystemUI/res/drawable/ic_noise_aware.xml
new file mode 100644
index 000000000000..54826414f3f7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_noise_aware.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M440,82Q450,81 460,80.5Q470,80 480,80Q491,80 500.5,80.5Q510,81 520,82L520,162Q510,160 500.5,160Q491,160 480,160Q469,160 459.5,160Q450,160 440,162L440,82ZM272,138Q289,127 306.5,119Q324,111 343,104L378,176Q358,182 340.5,190.5Q323,199 306,210L272,138ZM654,210Q637,199 619.5,190.5Q602,182 582,176L617,104Q636,111 653.5,119Q671,127 688,138L654,210ZM753,311Q742,294 729,278.5Q716,263 702,249L765,199Q779,213 792,228.5Q805,244 816,261L753,311ZM143,263Q154,246 166.5,230.5Q179,215 193,201L256,251Q242,265 229.5,280.5Q217,296 206,313L143,263ZM83,428Q85,408 90,388.5Q95,369 101,350L180,368Q173,387 168.5,406.5Q164,426 162,446L83,428ZM799,449Q797,429 792.5,409Q788,389 781,370L859,352Q865,371 870,390.5Q875,410 877,430L799,449ZM781,590Q788,571 792,552Q796,533 798,513L877,531Q875,551 870,570.5Q865,590 859,609L781,590ZM162,514Q164,534 168.5,553.5Q173,573 180,592L101,610Q95,591 90,571.5Q85,552 83,532L162,514ZM705,708Q719,694 731,678.5Q743,663 754,646L818,696Q807,713 794.5,728.5Q782,744 768,758L705,708ZM194,760Q180,746 167.5,730Q155,714 144,697L206,647Q217,664 229.5,680Q242,696 256,710L194,760ZM583,783Q603,776 620,768Q637,760 654,749L689,821Q672,832 654.5,840.5Q637,849 618,856L583,783ZM344,857Q325,850 307,841.5Q289,833 272,822L307,750Q324,761 341.5,769.5Q359,778 379,784L344,857ZM480,880Q470,880 460,879.5Q450,879 440,878L440,798Q453,800 480,800Q491,800 500.5,800Q510,800 520,798L520,878Q510,879 500.5,879.5Q491,880 480,880ZM520,720Q482,720 450.5,697Q419,674 406,638Q403,629 399.5,620.5Q396,612 389,605L334,550Q308,524 294,490.5Q280,457 280,420Q280,345 332.5,292.5Q385,240 460,240Q529,240 580,285.5Q631,331 639,400L558,400Q551,365 523.5,342.5Q496,320 460,320Q418,320 389,349Q360,378 360,420Q360,440 368,459.5Q376,479 391,494L445,548Q459,562 467.5,578.5Q476,595 482,612Q487,625 497,632.5Q507,640 520,640Q537,640 548.5,628.5Q560,617 560,600L640,600Q640,650 605.5,685Q571,720 520,720ZM540,560Q515,560 497.5,542.5Q480,525 480,500Q480,474 497.5,457Q515,440 540,440Q566,440 583,457Q600,474 600,500Q600,525 583,542.5Q566,560 540,560Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/media_squiggly_progress.xml b/packages/SystemUI/res/drawable/media_squiggly_progress.xml
index 9cd3f6288b1d..ae797f763b77 100644
--- a/packages/SystemUI/res/drawable/media_squiggly_progress.xml
+++ b/packages/SystemUI/res/drawable/media_squiggly_progress.xml
@@ -14,4 +14,4 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.media.controls.ui.SquigglyProgress /> \ No newline at end of file
+<com.android.systemui.media.controls.ui.drawable.SquigglyProgress /> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_media_background.xml b/packages/SystemUI/res/drawable/qs_media_background.xml
index 217656dab022..830c882e1010 100644
--- a/packages/SystemUI/res/drawable/qs_media_background.xml
+++ b/packages/SystemUI/res/drawable/qs_media_background.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.systemui.media.controls.ui.IlluminationDrawable
+<com.android.systemui.media.controls.ui.drawable.IlluminationDrawable
xmlns:systemui="http://schemas.android.com/apk/res-auto"
systemui:highlight="15"
systemui:cornerRadius="@dimen/notification_corner_radius" /> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_media_light_source.xml b/packages/SystemUI/res/drawable/qs_media_light_source.xml
index 849349a5f100..0b42dbab6ced 100644
--- a/packages/SystemUI/res/drawable/qs_media_light_source.xml
+++ b/packages/SystemUI/res/drawable/qs_media_light_source.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.media.controls.ui.LightSourceDrawable
+<com.android.systemui.media.controls.ui.drawable.LightSourceDrawable
xmlns:systemui="http://schemas.android.com/apk/res-auto"
systemui:rippleMinSize="25dp"
systemui:rippleMaxSize="135dp" /> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/anc_slice.xml b/packages/SystemUI/res/layout/anc_slice.xml
new file mode 100644
index 000000000000..71752f217a4f
--- /dev/null
+++ b/packages/SystemUI/res/layout/anc_slice.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<androidx.slice.widget.SliceView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/slice_view"
+ style="@style/Widget.SliceView.Panel.Slider"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" /> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_carousel.xml b/packages/SystemUI/res/layout/media_carousel.xml
index 715c86957e02..825ece856ed2 100644
--- a/packages/SystemUI/res/layout/media_carousel.xml
+++ b/packages/SystemUI/res/layout/media_carousel.xml
@@ -24,7 +24,7 @@
android:clipToPadding="false"
android:forceHasOverlappingRendering="false"
android:theme="@style/MediaPlayer">
- <com.android.systemui.media.controls.ui.MediaScrollView
+ <com.android.systemui.media.controls.ui.view.MediaScrollView
android:id="@+id/media_carousel_scroller"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -42,7 +42,7 @@
>
<!-- QSMediaPlayers will be added here dynamically -->
</LinearLayout>
- </com.android.systemui.media.controls.ui.MediaScrollView>
+ </com.android.systemui.media.controls.ui.view.MediaScrollView>
<com.android.systemui.qs.PageIndicator
android:id="@+id/media_page_indicator"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/scene_window_root.xml b/packages/SystemUI/res/layout/scene_window_root.xml
index 0dcd15b429c1..bb8de4c32e76 100644
--- a/packages/SystemUI/res/layout/scene_window_root.xml
+++ b/packages/SystemUI/res/layout/scene_window_root.xml
@@ -24,7 +24,7 @@
android:id="@+id/scene_window_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="true">
+ android:fitsSystemWindows="false">
<include layout="@layout/super_notification_shade"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
index 37964158a4aa..ae81d2962ff3 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -67,7 +67,7 @@
android:gravity="start"/>
<!-- Buttons -->
- <LinearLayout
+ <com.android.internal.widget.ButtonBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@@ -90,6 +90,6 @@
android:layout_weight="0"
android:text="@string/screenrecord_continue"
style="@style/Widget.Dialog.Button" />
- </LinearLayout>
+ </com.android.internal.widget.ButtonBarLayout>
</LinearLayout>
</ScrollView> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index d05a1fcfaaf4..4002517b2cd4 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gestoor"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ontkoppel"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveer"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Skakel dit môre outomaties weer aan"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Kenmerke soos Kitsdeel, Kry My Toestel en toestelligging gebruik Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterykrag"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Oudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kopstuk"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Hierdie toestel word deur jou ouer bestuur. Jou ouer kan inligting sien en bestuur soos die programme wat jy gebruik, jou ligging en jou skermtyd."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ontsluit gehou deur TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Diefstalbeskerming\nToestel gesluit; te veel pogings om te ontsluit"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Klankinstellings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Gee outomaties mediaopskrifte"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"deaktiveer"</string>
<string name="sound_settings" msgid="8874581353127418308">"Klank en vibrasie"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Instellings"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Intydse Onderskrifte"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume is verlaag na ’n veiliger vlak"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Oorfoonvolume was langer as wat aanbeveel word hoog"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Oorfoonvolume het die veilige limiet vir hierdie week oorskry"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreer"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s volumekontroles"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Oproepe en kennisgewings sal lui (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> speel tans op"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Oudio sal speel op"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Stelsel-UI-ontvanger"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusbalk"</string>
<string name="demo_mode" msgid="263484519766901593">"Stelsel-UI-demonstrasiemodus"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 6ee6c996e70b..ebc20027209f 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ተቀምጧል"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ግንኙነትን አቋርጥ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ያግብሩ"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ነገ እንደገና በራስ-ሰር አስጀምር"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"እንደ ፈጣን ማጋራት፣ የእኔን መሣሪያ አግኝ እና የመሣሪያ አካባቢ ያሉ ባህሪያት ብሉቱዝን ይጠቀማሉ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ባትሪ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ኦዲዮ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ማዳመጫ"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ይህ መሣሪያ በእርስዎ ወላጅ የሚተዳደር ነው። ወላጅዎ የሚጠቀሙባቸውን መተግበሪያዎች፣ አካባቢዎን እና የማያ ገፅ ጊዜዎን የመሳሰሉ መረጃዎችን ማየት እና ማስተዳደር ይችላል።"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"በ TrustAgent እንደተከፈተ ቀርቷል"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"የስርቆት መከላከያ\nመሳሪያ ተቆልፏል፣ በጣም ብዙ የመክፈት ሙከራዎች"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>። <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"የድምፅ ቅንብሮች"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ራስሰር የሥዕል መግለጫ ጽሑፍን ሚዲያ"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ንዘር"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s የድምፅ መቆጣጠሪያዎች"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"ጥሪዎች እና ማሳወቂያዎች (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>) ላይ ይደውላሉ"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> እየተጫወተ ያለው በ"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ኦዲዮ ይጫወታል በ"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"የስርዓት በይነገጽ መቃኛ"</string>
<string name="status_bar" msgid="4357390266055077437">"የሁኔታ አሞሌ"</string>
<string name="demo_mode" msgid="263484519766901593">"የስርዓት ተጠቃሚ በይነገጽ ማሳያ ሁነታ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index fdad1cfe9b38..ec72578f9fc3 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"إلغاء الربط"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"تفعيل"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"تفعيل البلوتوث تلقائيًا مرة أخرى غدًا"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"‏يُستخدَم البلوتوث في ميزات مثل Quick Share و\"العثور على جهازي\" والموقع الجغرافي للجهاز"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"سماعة الرأس"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"يتولّى أحد الوالدين إدارة هذا الجهاز. يمكن للوالدين عرض وإدارة معلوماتك، مثلاً التطبيقات التي تستخدمها وموقعك الجغرافي ووقت النظر إلى الشاشة."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"شبكة افتراضية خاصة"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"‏فتح القفل باستمرار بواسطة TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"تم قفل الجهاز\nلحمايته من السرقة بسبب إجراء العديد من محاولات إلغاء القفل."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"إعدادات الصوت"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"شرح تلقائي للوسائط"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"اهتزاز"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"‏%s عنصر للتحكم في مستوى الصوت"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"سيصدر الهاتف رنينًا عند تلقي المكالمات والإشعارات (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)."</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"تشغيل <xliff:g id="LABEL">%s</xliff:g> على"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"سيتم تشغيل الصوت على"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"أداة ضبط واجهة مستخدم النظام"</string>
<string name="status_bar" msgid="4357390266055077437">"شريط الحالة"</string>
<string name="demo_mode" msgid="263484519766901593">"وضع تجريبي لواجهة مستخدم النظام"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 34141ebde3df..f66650b41451 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ছেভ কৰা হৈছে"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"সংযোগ বিচ্ছিন্ন কৰক"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"সক্ৰিয় কৰক"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"কাইলৈ পুনৰ স্বয়ংক্ৰিয়ভাৱে অন কৰক"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Quick Share, Find My Device আৰু ডিভাইচৰ অৱস্থানৰ দৰে সুবিধাই ব্লুটুথ ব্যৱহাৰ কৰে"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিঅ’"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডছেট"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"এই ডিভাইচটো আপোনাৰ অভিভাৱকে পৰিচালনা কৰে। আপোনাৰ অভিভাৱকে আপুনি ব্যৱহাৰ কৰা এপ্‌, আপোনাৰ অৱস্থান আৰু আপুনি ডিভাইচত অতিবাহিত কৰা সময়ৰ দৰে তথ্য চাব আৰু পৰিচালনা কৰিব পাৰে।"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"ভিপিএন"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgentএ আনলক কৰি ৰাখিছে"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"চুৰিৰ পৰা সুৰক্ষা\nডিভাইচ লক আছে, বহুবাৰ আনলকৰ চেষ্টা কৰা হৈছে"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ধ্বনিৰ ছেটিং"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"স্বয়ংক্ৰিয় কেপশ্বন মিডিয়া"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"কম্পন কৰক"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ধ্বনি নিয়ন্ত্ৰণসমূহ"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"কল আৰু জাননীবোৰ ইমান ভলিউমত বাজিব (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"ইয়াত <xliff:g id="LABEL">%s</xliff:g> প্লে’ হৈ আছে"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"অডিঅ’ ইয়াত প্লে’ হ’ব"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"স্থিতি দণ্ড"</string>
<string name="demo_mode" msgid="263484519766901593">"ছিষ্টেমৰ UI প্ৰদৰ্শন ম\'ড"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 46dcc95db646..70484414a4cb 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Yadda saxlandı"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"əlaqəni kəsin"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivləşdirin"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Sabah avtomatik aktiv edin"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Cəld Paylaşım, Cihazın Tapılması və cihaz məkanı kimi funksiyalar Bluetooth istifadə edir"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batareya"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Qulaqlıq"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Bu cihaz valideyniniz tərəfindən idarə olunur. Valideyniniz işlətdiyiniz tətbiqlər, məkanınız və ekran vaxtınız kimi bilgiləri görə və idarə edə bilər."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN (Virtual Şəxsi Şəbəkələr)"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ilə açıq saxlayın"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Oğurluqdan qoruma\nCihaz kilidləndi. Çoxlu kilidaçma cəhdi"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Səs ayarları"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Avtomatik başlıq mediası"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrasiya"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s səs nəzarətləri"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Çağrı və bildirişlər zəng çalacaq (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> tətbiqində oxudulur"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio oxudulacaq"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Status paneli"</string>
<string name="demo_mode" msgid="263484519766901593">"Sistem interfeysi: demorejim"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 8e49279b4af6..a598d8f3733b 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Ovim uređajem upravlja roditelj. Roditelj može da vidi informacije, kao što su aplikacije koje koristiš, tvoju lokaciju i vreme ispred ekrana, i da upravlja njima."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Pouzdani agent sprečava zaključavanje"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Zaštita od krađe\nZaklj. uređaj, previše pokušaja otključavanja"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Podešavanja zvuka"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatski titl za medije"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibracija"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Kontrole za jačinu zvuka za %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Melodija zvona za pozive i obaveštenja je uključena (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> se pušta na"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk se pušta na"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Tjuner za korisnički interfejs sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusna traka"</string>
<string name="demo_mode" msgid="263484519766901593">"Režim demonstracije za korisnički interfejs sistema"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index c4a1a66a2351..2b137fb63258 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Гэта прылада знаходзіцца пад кантролем бацькоў. Бацькі могуць праглядаць і кантраляваць тваю інфармацыю, напрыклад пра праграмы, якія ты выкарыстоўваеш, даныя пра тваё месцазнаходжанне і час карыстання прыладай."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Разблакіравана з дапамогай TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Абарона ад крадзяжу\nПрылада заблакіравана з-за няўдалых спроб"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Налады гуку"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Аўтаматычныя субцітры"</string>
@@ -539,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"адключыць"</string>
<string name="sound_settings" msgid="8874581353127418308">"Гук і вібрацыя"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Налады"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Аўтаматычныя субцітры"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Гучнасць паніжана да больш бяспечнага ўзроўню"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Гучнасць у навушніках была вялікай больш часу, чым рэкамендавана"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Гучнасць у навушніках перавысіла ліміт бяспечнага праслухоўвання на гэтым тыдні"</string>
@@ -584,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вібрыраваць"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Рэгулятар гучнасці %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Для выклікаў і апавяшчэнняў уключаны гук (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> прайграецца тут:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аўдыявыхад:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Наладка сістэмнага інтэрфейсу карыстальніка"</string>
<string name="status_bar" msgid="4357390266055077437">"Панэль стану"</string>
<string name="demo_mode" msgid="263484519766901593">"Рэжым дэманстрацыі сістэмнага інтэрфейсу карыстальніка"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index b75a7dfd5972..aa3da9caa6f9 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Запазено"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекратяване на връзката"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активиране"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично включване отново утре"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Bluetooth се използва от различни функции, като например „Бързо споделяне“, „Намиране на устройството ми“ и местоположението на устройството"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Това устройство се управлява от родителя ви. Той може да вижда и управлява информация, като например приложенията, които използвате, местоположението ви и времето на ползване."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Поддържа се отключено от надежден агент"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Защита от кражба\nУ-вото е закл., твърде много опити за откл."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Настройки за звука"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Медия с автоматични надписи"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"деактивиране"</string>
<string name="sound_settings" msgid="8874581353127418308">"Звук и вибриране"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Настройки"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Надписи на живо"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Силата на звука е намалена до по-безопасно ниво"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Нивото на силата на звука на слушалките е било високо по-дълго, отколкото е препоръчително"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Нивото на силата на звука на слушалките е надвишило безопасния лимит за тази седмица"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибриране"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Контроли за силата на звука – %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"При обаждания и известия устройството ще звъни (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Възпроизвеждане на <xliff:g id="LABEL">%s</xliff:g> на"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудиото ще се възпроизвежда на"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Тунер на системния потребителски интерфейс"</string>
<string name="status_bar" msgid="4357390266055077437">"Лента на състоянието"</string>
<string name="demo_mode" msgid="263484519766901593">"Демонстрационен режим на системния ПИ"</string>
@@ -1235,7 +1235,7 @@
<string name="assistant_attention_content_description" msgid="4166330881435263596">"Установено е присъствие на потребител"</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Задайте стандартно приложение за бележки от настройките"</string>
<string name="install_app" msgid="5066668100199613936">"Инсталиране на приложението"</string>
- <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"Прекарайте пръст нагоре, за да продължите"</string>
+ <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"Плъзнете нагоре, за да продължите"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Да се дублира ли на външния екран?"</string>
<string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Съдържанието на вътрешния ви дисплей ще бъде дублирано. Предният ви дисплей ще бъде изключен."</string>
<string name="mirror_display" msgid="2515262008898122928">"Дублиране на дисплея"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 54cf758c5613..12e24c0a3bd3 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"সেভ করা আছে"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ডিসকানেক্ট করুন"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"চালু করুন"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"আগামীকাল অটোমেটিক আবার চালু হবে"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"দ্রুত শেয়ার, Find My Device ও ডিভাইসের লোকেশন ব্লুটুথ ব্যবহার করে"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"চার্জ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিও"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডসেট"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"আপনার অভিভাবক এই ডিভাইস ম্যানেজ করেন। আপনার অভিভাবক আপনার ব্যবহার করা অ্যাপ, লোকেশন ও স্ক্রিন টাইমের মতো তথ্যগুলি দেখতে এবং ম্যানেজ করতে পারেন।"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent দিয়ে আনলক করে রাখা হয়েছে"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"চুরি থেকে সুরক্ষা\nডিভাইস লক করা আছে, আনলক করার জন্য অনেকবার চেষ্টা করা হয়েছে"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"সাউন্ড সেটিংস"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"অটোমেটিক মিডিয়া ক্যাপশন দেখুন"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"বন্ধ হবে"</string>
<string name="sound_settings" msgid="8874581353127418308">"সাউন্ড ও ভাইব্রেশন"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"সেটিংস"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"লাইভ ক্যাপশন"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"ভলিউম কমিয়ে আরও নিরাপদ মাত্রায় নামানো হয়েছে"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"সাজেস্ট করা সময়ের চেয়ে অতিরিক্ত সময় ধরে হেডফোনের ভলিউম বেশি করা আছে"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"এই সপ্তাহে হেডফোনের ভলিউম নিরাপদ মাত্রা ছাড়িয়ে গেছে"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ভাইব্রেট করান"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ভলিউম নিয়ন্ত্রণ"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"কল এবং বিজ্ঞপ্তির রিং হবে (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>-এ প্লে করা হচ্ছে"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"অডিও প্লে করা হবে"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"সিস্টেম UI টিউনার"</string>
<string name="status_bar" msgid="4357390266055077437">"স্ট্যাটাস বার"</string>
<string name="demo_mode" msgid="263484519766901593">"সিস্টেম UI ডেমো মোড"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 989daf970d7d..f79e2d915451 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekid veze"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovo sutra"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funkcije kao što su Quick Share, Pronađi moj uređaj i lokacija uređaja koriste Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Ovim uređajem upravlja tvoj roditelj. Roditelj može vidjeti i upravljati informacijama kao što su aplikacije koje koristiš, lokacija i vrijeme korištenja uređaja."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Pouzdani agent sprečava zaključavanje"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Zaštita od krađe\nUređaj je zaključan, previše pokušaja otključ."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Postavke zvuka"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatski titlovi za medije"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Kontrole glasnoće za %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Pozivi i obavještenja će zvoniti jačinom (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Reproduciranje: <xliff:g id="LABEL">%s</xliff:g>"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk će se reprod. na:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Podešavač za korisnički interfejs sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusna traka"</string>
<string name="demo_mode" msgid="263484519766901593">"Demo način rada Sistemskog UI-ja"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 6b0eabc50b69..583275f366b3 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"El teu pare o mare gestionen aquest dispositiu, i poden veure i gestionar informació com ara les aplicacions que utilitzes, la teva ubicació i el teu temps de connexió."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloquejat per TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protecció antirobatoris\nDispositiu bloquejat; massa intents"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Configuració del so"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Subtitula el contingut multimèdia automàticament"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controls de volum %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Les trucades i les notificacions sonaran (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"S\'està reproduint <xliff:g id="LABEL">%s</xliff:g> a"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Es reproduirà a"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Personalitzador d\'interfície d\'usuari"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra d\'estat"</string>
<string name="demo_mode" msgid="263484519766901593">"Mode de demostració de la IU del sistema"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 71c8a35fa08b..6cdb3d8af2a1 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Toto zařízení spravuje rodič. Rodič může zobrazit údaje, jako jsou používané aplikace, tvá poloha a čas strávený na zařízení."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Odemknutí udržováno funkcí TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Ochrana před odcizením\nZamknuto, moc pokusů o odemknutí"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Nastavení zvuku"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatické přepisy médií"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrovat"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Ovládací prvky hlasitosti %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Volání a oznámení budou vyzvánět (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Přehrávání <xliff:g id="LABEL">%s</xliff:g> na"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk se přehraje na"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Nástroj na ladění uživatelského rozhraní systému"</string>
<string name="status_bar" msgid="4357390266055077437">"Stavový řádek"</string>
<string name="demo_mode" msgid="263484519766901593">"Ukázkový režim uživatelského rozhraní systému"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index ab436790cc1b..79b01a84b3bb 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gemt"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"afbryd forbindelse"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivér"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivér automatisk igen i morgen"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funktioner som f.eks. Quick Share, Find min enhed og enhedslokation anvender Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Denne enhed administreres af din forælder. Din forælder kan se og administrere oplysninger såsom de apps, du bruger, din lokation og din skærmtid."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Holdes oplåst af TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Tyveribeskyttelse\nLåst enhed (for mange oplåsningsforsøg)"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Lydindstillinger"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Undertekster til medier"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"deaktiver"</string>
<string name="sound_settings" msgid="8874581353127418308">"Lyd og vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Indstillinger"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Livetekstning"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Lydstyrken blev reduceret til et mere sikkert niveau"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Høretelefonernes lydstyrke har været høj i længere tid end anbefalet"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Høretelefonernes lydstyrke har overskredet sikkerhedsgrænsen for denne uge"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s lydstyrkeknapper"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Der afspilles lyd ved opkald og notifikationer (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Afspiller <xliff:g id="LABEL">%s</xliff:g> på"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Lyden afspilles på"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusbjælke"</string>
<string name="demo_mode" msgid="263484519766901593">"Demotilstand for systemets brugerflade"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 3c4730038040..43f799bd1b68 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gespeichert"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Verknüpfung aufheben"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivieren"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen automatisch wieder aktivieren"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Für Funktionen wie Quick Share, „Mein Gerät finden“ und den Gerätestandort wird Bluetooth verwendet"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Dieses Gerät wird von deinen Eltern verwaltet. Sie können unter anderem Informationen über deine genutzten Apps, deinen Standort und deine Bildschirmzeit einsehen und verwalten."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Durch TrustAgent entsperrt"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Diebstahlschutz\nGerät gesperrt – zu viele Entsperrversuche"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Toneinstellungen"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Medien autom. untertiteln"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"deaktivieren"</string>
<string name="sound_settings" msgid="8874581353127418308">"Ton &amp; Vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Einstellungen"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Automatische Untertitel"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Auf verträglichere Lautstärke eingestellt"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Die Kopfhörerlautstärke war länger als empfohlen hoch eingestellt"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Die Kopfhörerlautstärke hat für diese Woche das Sicherheitslimit überschritten"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"Vibrieren lassen"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Lautstärkeregler von %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Gerät klingelt bei Anrufen und Benachrichtigungen (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Wiedergabe von <xliff:g id="LABEL">%s</xliff:g> über"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audiowiedergabe über"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusleiste"</string>
<string name="demo_mode" msgid="263484519766901593">"Demomodus der System-UI"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index a7a300f391e1..43ab777aa1de 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Αποθηκεύτηκε"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"αποσύνδεση"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ενεργοποίηση"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Αυτόματη ενεργοποίηση ξανά αύριο"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Λειτουργίες όπως το Quick Share, η Εύρεση συσκευής και η τοποθεσία της συσκευής χρησιμοποιούν Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ήχος"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ακουστικά"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Αυτή η συσκευή είναι διαχειριζόμενη από τον γονέα σου. Ο γονέας σου μπορεί να βλέπει και να διαχειρίζεται πληροφορίες όπως οι εφαρμογές που χρησιμοποιείς, η τοποθεσία σου και ο χρόνος χρήσης."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Διατήρηση ξεκλειδώματος με TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Προστασία από κλοπή\nΗ συσκ. κλειδ., πάρα πολλές προσπ. ξεκλ."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ρυθμίσεις ήχου"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Αυτόματοι υπότιτλοι στο μέσο"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"δόνηση"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s στοιχεία ελέγχου έντασης ήχου"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Θα υπάρχει ηχητική ειδοποίηση για κλήσεις και ειδοποιήσεις (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Αναπαραγωγή <xliff:g id="LABEL">%s</xliff:g> σε"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Ο ήχος θα παίξει σε"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Γραμμή κατάστασης"</string>
<string name="demo_mode" msgid="263484519766901593">"Λειτουργία επίδειξης διεπαφής χρήστη συστήματος"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 40ad5af31440..9d2742a2b9fe 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Features like Quick Share, Find My Device and device location use Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"This device is managed by your parent. Your parent can see and manage information such as the apps that you use, your location and your screen time."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by trust agent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Theft protection\nDevice locked, too many unlock attempts"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatically caption media"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"disable"</string>
<string name="sound_settings" msgid="8874581353127418308">"Sound and vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Settings"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Live Caption"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume lowered to safer level"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Headphone volume has been high for longer than recommended"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Headphone volume has exceeded the safe limit for this week"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s volume controls"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Calls and notifications will ring (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Playing <xliff:g id="LABEL">%s</xliff:g> on"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio will play on"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Status bar"</string>
<string name="demo_mode" msgid="263484519766901593">"System UI demo mode"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index a51225a1b26f..f77f66028397 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"This device is managed by your parent. Your parent can see and manage information such as the apps you use, your location, and your screen time."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Theft protection\nDevice locked, too many unlock attempts"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatically caption media"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s volume controls"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Calls and notifications will ring (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Playing <xliff:g id="LABEL">%s</xliff:g> on"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio will play on"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Status bar"</string>
<string name="demo_mode" msgid="263484519766901593">"System UI demo mode"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 40ad5af31440..9d2742a2b9fe 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Features like Quick Share, Find My Device and device location use Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"This device is managed by your parent. Your parent can see and manage information such as the apps that you use, your location and your screen time."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by trust agent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Theft protection\nDevice locked, too many unlock attempts"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatically caption media"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"disable"</string>
<string name="sound_settings" msgid="8874581353127418308">"Sound and vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Settings"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Live Caption"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume lowered to safer level"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Headphone volume has been high for longer than recommended"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Headphone volume has exceeded the safe limit for this week"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s volume controls"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Calls and notifications will ring (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Playing <xliff:g id="LABEL">%s</xliff:g> on"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio will play on"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Status bar"</string>
<string name="demo_mode" msgid="263484519766901593">"System UI demo mode"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 40ad5af31440..9d2742a2b9fe 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Features like Quick Share, Find My Device and device location use Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"This device is managed by your parent. Your parent can see and manage information such as the apps that you use, your location and your screen time."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by trust agent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Theft protection\nDevice locked, too many unlock attempts"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatically caption media"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"disable"</string>
<string name="sound_settings" msgid="8874581353127418308">"Sound and vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Settings"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Live Caption"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume lowered to safer level"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Headphone volume has been high for longer than recommended"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Headphone volume has exceeded the safe limit for this week"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s volume controls"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Calls and notifications will ring (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Playing <xliff:g id="LABEL">%s</xliff:g> on"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio will play on"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Status bar"</string>
<string name="demo_mode" msgid="263484519766901593">"System UI demo mode"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index d98ef62cad85..6f5c13362405 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎This device is managed by your parent. Your parent can see and manage information such as the apps you use, your location, and your screen time.‎‏‎‎‏‎"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‎‏‎‎‎‏‎‏‏‎‎‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‏‎‏‎‎‏‎VPN‎‏‎‎‏‎"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‏‎‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‏‎Kept unlocked by TrustAgent‎‏‎‎‏‎"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‎‏‏‏‎‏‎‎‏‎‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎Theft protection‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Device locked, too many unlock attempts‎‏‎‎‏‎"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‎‎‎‎‎‏‎‏‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="ZEN_MODE">%1$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‎‎Sound settings‎‏‎‎‏‎"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‎‏‏‎‏‏‏‎‏‎Automatically caption media‎‏‎‎‏‎"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‎‏‎‎‏‎‏‎vibrate‎‏‎‎‏‎"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‏‏‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‏‏‎‎‎‏‎‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‏‎‎‎%s volume controls‎‏‎‎‏‎"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‎‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‏‎‏‎‎Calls and notifications will ring (‎‏‎‎‏‏‎<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‎Playing ‎‏‎‎‏‏‎<xliff:g id="LABEL">%s</xliff:g>‎‏‎‎‏‏‏‎ on‎‏‎‎‏‎"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‎‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎Audio will play on‎‏‎‎‏‎"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎‏‏‎‎‎‎‎‎‎‏‏‎‏‎‏‎‎‏‎System UI Tuner‎‏‎‎‏‎"</string>
<string name="status_bar" msgid="4357390266055077437">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‏‏‏‏‎‏‎Status bar‎‏‎‎‏‎"</string>
<string name="demo_mode" msgid="263484519766901593">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎‎‏‎‏‎‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‎‎‎‏‏‎‏‎‏‏‎‎‏‎System UI demo mode‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index c3ade659938d..8c64a0767cfd 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Tu padre o madre administra este dispositivo. Esa persona puede ver y administrar información, como las apps que usas, tu ubicación y el tiempo de uso."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent lo mantiene desbloqueado"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protección antirrobo\nDispositivo bloqueado; muchos intentos"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Configuración de sonido"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Muestra subtítulos automáticos"</string>
@@ -539,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"inhabilitar"</string>
<string name="sound_settings" msgid="8874581353127418308">"Sonido y vibración"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Configuración"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Subtitulado instantáneo"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Se bajó el volumen a un nivel seguro"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"El volumen de los auriculares se mantuvo elevado por más tiempo del recomendado"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Se excedió el límite seguro de volumen de los auriculares para esta semana"</string>
@@ -584,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controles de volumen %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Sonarán las llamadas y notificaciones (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Reproduciendo <xliff:g id="LABEL">%s</xliff:g> en"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Se reproducirá en"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador de IU del sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra de estado"</string>
<string name="demo_mode" msgid="263484519766901593">"Modo demostración de la IU del sistema"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 73dcea18ebc2..692bb26fb190 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Tu padre o madre gestionan este dispositivo y pueden ver y controlar cierta información, como las aplicaciones que utilizas, tu ubicación y tu tiempo de pantalla."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloqueado por TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protección antirrobo\nDispositivo bloqueado; demasiados intentos"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ajustes de sonido"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Subtitular automáticamente"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controles de volumen %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Las llamadas y las notificaciones sonarán (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Reproduciendo <xliff:g id="LABEL">%s</xliff:g> en"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Se reproducirá en"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Configurador de UI del sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra de estado"</string>
<string name="demo_mode" msgid="263484519766901593">"Modo Demo de UI del sistema"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 0e1e690b2733..070f200f2d74 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvestatud"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkesta ühendus"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveeri"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Lülita automaatselt homme uuesti sisse"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funktsioonid, nagu Kiirjagamine, Leia mu seade ja seadme asukoht, kasutavad Bluetoothi"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> akut"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Heli"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Peakomplekt"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Seda seadet haldab sinu vanem. Sinu vanem näeb ja saab hallata teavet, näiteks kasutatavaid rakendusi, sinu asukohta ja ekraaniaega."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Avatuna hoiab TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Vargusvastane kaitse\nSeade lukus – liiga palju avamiskatseid"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Heliseaded"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automaatsed subtiitrid"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreerimine"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Helitugevuse juhtnupud: %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Kõnede ja märguannete puhul telefon heliseb (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Esitamine jätkub seadmes <xliff:g id="LABEL">%s</xliff:g>"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Heli esitamine jätkub"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Süsteemi kasutajaliidese tuuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Olekuriba"</string>
<string name="demo_mode" msgid="263484519766901593">"Süsteemi kasutajaliidese demorežiim"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index f6a4f395c055..36eac0ce42b9 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gordeta"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deskonektatu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktibatu"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktibatu automatikoki berriro bihar"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Quick Share, Bilatu nire gailua, gailuaren kokapena eta beste eginbide batzuek Bluetootha darabilte"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audioa"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Entzungailua"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Zure gurasoak kudeatzen du gailua. Zure gurasoak gailuko informazioa ikusi eta kudea dezake; besteak beste, zer aplikazio erabiltzen dituzun, zure kokapena zein den eta pantaila aurrean zenbat eta noiz egoten zaren."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPNa"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent bidez desblokeatuta"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Lapurreten aurkako babesa\nGailua blokeatuta dago, desblokeatzeko saiakera gehiegi"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Soinuaren ezarpenak"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Ezarri azpitituluak automatikoki"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"desgaitu"</string>
<string name="sound_settings" msgid="8874581353127418308">"Audioa eta dardara"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Ezarpenak"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Istanteko azpitituluak"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Bolumena maila seguruago batera jaitsi da"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Entzungailuen bolumena gomendatutako denbora baino luzaroago egon da ozen"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Entzungailuen bolumenak aste honetarako muga segurua gainditu du"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dardara"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s gailuaren bolumena kontrolatzeko aukerak"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Tonuak jo egingo du deiak eta jakinarazpenak jasotzean (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> hemen erreproduzitzen:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audioa erreproduzitzen jarraituko du"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sistemaren erabiltzaile-interfazearen konfiguratzailea"</string>
<string name="status_bar" msgid="4357390266055077437">"Egoera-barra"</string>
<string name="demo_mode" msgid="263484519766901593">"Sistemaren erabiltzaile-interfazearen demo modua"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 7a29b27ca705..b3a5e36327db 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ذخیره‌شده"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"قطع اتصال"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کردن"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"فردا دوباره به‌طور خودکار روشن شود"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ویژگی‌هایی مثل «هم‌رسانی سریع»، «پیدا کردن دستگاهم»، و مکان دستگاه از بلوتوث استفاده می‌کنند"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"شارژ باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"هدست"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"این دستگاه را ولی‌تان مدیریت می‌کند. ولی‌تان می‌تواند اطلاعاتی مثل برنامه‌هایی که استفاده می‌کنید، مکانتان، و مدت تماشای صفحه‌تان را ببیند و مدیریت کند."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"‏با TrustAgent قفل را باز نگه‌دارید"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"محافظت دربرابر سرقت\nدستگاه قفل شد، تعداد تلاش‌ها قفل‌گشایی از حد مجاز بیشتر بود"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. ‏<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"تنظیمات صدا"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"رسانه زیرنویس خودکار"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"لرزش"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"‏%s کنترل‌های میزان صدا"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"تماس‌ها و اعلان‌ها زنگ می‌خورند (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"درحال پخش <xliff:g id="LABEL">%s</xliff:g> در"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"صدا پخش می‌شود در"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"تنظیم‌کننده واسط کاربری سیستم"</string>
<string name="status_bar" msgid="4357390266055077437">"نوار وضعیت"</string>
<string name="demo_mode" msgid="263484519766901593">"حالت نمایشی واسط کاربری سیستم"</string>
@@ -1234,7 +1235,7 @@
<string name="assistant_attention_content_description" msgid="4166330881435263596">"حضور کاربر شناسایی می‌شود"</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"برنامه پیش‌فرض یادداشت را در «تنظیمات» تنظیم کنید"</string>
<string name="install_app" msgid="5066668100199613936">"نصب برنامه"</string>
- <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"برای ادامه دادن تند به‌بالا بکشید"</string>
+ <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"برای ادامه دادن، تند به‌بالا بکشید"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"روی نمایشگر خارجی قرینه‌سازی شود؟"</string>
<string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"نمایشگر داخلی شما قرینه‌سازی می‌شود. نمایشگر جلو خاموش می‌شود."</string>
<string name="mirror_display" msgid="2515262008898122928">"قرینه‌سازی نمایشگر"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 8f85edfd309b..47e7f2d7dd2d 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Tallennettu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkaise yhteys"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivoi"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Laita automaattisesti päälle taas huomenna"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Ominaisuudet (esim. Quick Share ja Paikanna laite) ja laitteen sijainti käyttävät Bluetoothia"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ääni"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Vanhempasi ylläpitää tätä laitetta. Vanhempasi voi nähdä ja ylläpitää tietoja, esim. käyttämiäsi sovelluksia, sijaintiasi ja käyttöaikaasi."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent pitää lukitusta avattuna"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Varkaussuoja\nLaite lukittu, liian monta avausyritystä"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ääniasetukset"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Tekstitä media automaatt."</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"poista käytöstä"</string>
<string name="sound_settings" msgid="8874581353127418308">"Ääni ja värinä"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Asetukset"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Livetekstitys"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Äänenvoimakkuus laskettu turvalliselle tasolle"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Äänenvoimakkuus on ollut suuri suositeltua kauemmin"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Kuulokkeiden äänenvoimakkuus on ylittänyt tämän viikon turvarajan"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"värinä"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Äänenvoimakkuuden säädin: %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Puhelut ja ilmoitukset soivat (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Toistetaan: <xliff:g id="LABEL">%s</xliff:g>"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audiota toistetaan laitteella"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Tilapalkki"</string>
<string name="demo_mode" msgid="263484519766901593">"Käyttöliittymän esittelytila"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index e95955589a22..1bb8147b9cdf 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Déconnecter"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"Activer"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activer le Bluetooth automatiquement demain"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Les fonctionnalités comme le Partage rapide, Localiser mon appareil et la position de l\'appareil utilisent le Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Écouteurs"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Cet appareil est géré par ton parent. Ton parent peut voir et gérer de l\'information, comme les applications que tu utilises, ta position et ton temps d\'utilisation des écrans."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"RPV"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Maintenu déverrouillé par TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protection c. le vol\nAppareil verrouillé, trop de tentatives"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Paramètres sonores"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sous-titrer automatiquement"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"désactiver"</string>
<string name="sound_settings" msgid="8874581353127418308">"Son et vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Paramètres"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Sous-titres instantanés"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume réduit à un niveau plus sécuritaire"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Le niveau du volume des écouteurs est resté élevé au-delà de la durée recommandée"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Le niveau du volume des écouteurs a dépassé la limite de sécurité pour cette semaine"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Commandes de volume de %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Les appels et les notifications seront annoncés par une sonnerie (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Lecture de <xliff:g id="LABEL">%s</xliff:g> sur"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Lecture audio sur"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Barre d\'état"</string>
<string name="demo_mode" msgid="263484519766901593">"Mode Démo de l\'interface système"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index a04f6e8800ee..84160af98955 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -81,7 +81,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Essayez de nouveau de faire une capture d\'écran"</string>
<string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"Impossible d\'enregistrer la capture d\'écran"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Les captures d\'écran ne sont pas autorisées par l\'application ni par votre organisation"</string>
- <string name="screenshot_blocked_by_admin" msgid="5486757604822795797">"La prise de captures d\'écran est bloquée par votre administrateur informatique"</string>
+ <string name="screenshot_blocked_by_admin" msgid="5486757604822795797">"La capture d\'écran est bloquée par votre administrateur informatique"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Modifier"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Modifier la capture d\'écran"</string>
<string name="screenshot_share_description" msgid="2861628935812656612">"Partager la capture d\'écran"</string>
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Cet appareil est géré par tes parents. Ils peuvent voir et gérer certaines informations, telles que les applications que tu utilises, ta position et ton temps d\'utilisation de l\'appareil."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Maintenu déverrouillé par TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protection contre le vol\nAppareil verrouillé, trop de tentatives de déverrouillage"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Paramètres audio"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sous-titrer automatiquement"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"activer le vibreur"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Commandes de volume %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Les appels et les notifications sonneront (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Diffusion de <xliff:g id="LABEL">%s</xliff:g> sur"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"L\'audio se mettra en marche"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Barre d\'état"</string>
<string name="demo_mode" msgid="263484519766901593">"Mode démo de l\'UI du système"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 2adb759cc555..b78199a149cf 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gardouse"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver activar automaticamente mañá"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"As funcións como Quick Share, Localizar o meu dispositivo ou a de localización do dispositivo utilizan o Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"O teu pai ou nai xestiona este dispositivo e pode ver e xestionar información como as aplicacións que usas, a túa localización e o tempo diante da pantalla."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloqueado por un axente de confianza"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protección antirroubo\nDisp. bloq., demasiados intentos desb."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Configuración do son"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Crear subtítulos automáticos"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"desactiva"</string>
<string name="sound_settings" msgid="8874581353127418308">"Son e vibración"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Configuración"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Subtítulos instantáneos"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"O volume baixouse ata un nivel máis seguro"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Usaches os auriculares cun volume alto durante máis tempo do recomendado"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"O volume dos auriculares superou o límite de seguranza desta semana"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controis de volume de %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"As chamadas e as notificacións soarán (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Reproducindo <xliff:g id="LABEL">%s</xliff:g> en"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio reproducido en"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Configurador da IU do sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra de estado"</string>
<string name="demo_mode" msgid="263484519766901593">"Modo de demostración da IU do sistema"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index e61a5b52beb9..d84b1f553f8b 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"સાચવેલું"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ડિસ્કનેક્ટ કરો"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"સક્રિય કરો"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"આવતીકાલે ફરીથી ઑટોમૅટિક રીતે ચાલુ કરો"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ક્વિક શેર, Find My Device અને ડિવાઇસના લોકેશન જેવી સુવિધાઓ બ્લૂટૂથનો ઉપયોગ કરે છે"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> બૅટરી"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ઑડિયો"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"હૅડસેટ"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"આ ડિવાઇસ તમારા માતાપિતા દ્વારા મેનેજ કરવામાં આવે છે. તમે જેનો ઉપયોગ કરો છો તે ઍપ, તમારું સ્થાન અને તમારા સ્ક્રીન સમય જેવી માહિતીને તમારા માતાપિતા જોઈ અને મેનેજ કરી શકે છે."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent દ્વારા અનલૉક રાખેલું"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"ચોરીથી સુરક્ષા\nડિવાઇસ અનલૉક કર્યું, અનલૉક કરવાના ઘણા પ્રયાસો"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"સાઉન્ડ સેટિંગ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"મીડિયામાં કૅપ્શન ઑટોમૅટિક રીતે ઉમેરો"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"બંધ કરો"</string>
<string name="sound_settings" msgid="8874581353127418308">"સાઉન્ડ અને વાઇબ્રેશન"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"સેટિંગ"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"લાઇવ કૅપ્શન"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"વૉલ્યૂમને વધુ સલામત લેવલ સુધી ઘટાડ્યું"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"હૅડફોનનું વૉલ્યૂમ સુઝાવ આપેલા સમય કરતાં વધારે સમય સુધી ઊંચા વૉલ્યૂમ પર રહ્યું છે"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"હૅડફોનનું વૉલ્યૂમ આ અઠવાડિયા માટેની સુરક્ષિત મર્યાદા કરતાં વધારે છે"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"વાઇબ્રેટ"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s વૉલ્યૂમ નિયંત્રણો"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"કૉલ અને નોટિફિકેશનની રિંગ (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>) પર વાગશે"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> વગાડી રહ્યાં છીએ"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ઑડિયો આની પર વાગશે"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"સિસ્ટમ UI ટ્યૂનર"</string>
<string name="status_bar" msgid="4357390266055077437">"સ્ટેટસ બાર"</string>
<string name="demo_mode" msgid="263484519766901593">"સિસ્ટમ UI ડેમો મોડ"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index cf029e0bc64d..8d1ac11b933f 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव किया गया"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिसकनेक्ट करें"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"चालू करें"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"कल फिर से अपने-आप चालू हो जाएगा"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"क्विक शेयर, Find My Device, और डिवाइस की जगह की जानकारी जैसी सुविधाएं ब्लूटूथ का इस्तेमाल करती हैं"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बैटरी"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडियो"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"इस डिवाइस का प्रबंधन आपके अभिभावक करते हैं. अभिभावक आपके डिवाइस से जुड़ी जानकारी देख सकते हैं. साथ ही, इसे प्रबंधित कर सकते हैं. इनमें आपके इस्तेमाल किए गए ऐप्लिकेशन, जगह की जानकारी, और डिवाइस के इस्तेमाल में बिताए गए समय जैसी जानकारी शामिल है."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"वीपीएन"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent की वजह से अनलॉक रखा गया है"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"चोरी से सुरक्षा\nडिवाइस लॉक हो गया, अनलॉक की कई कोशिशें की गईं"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"साउंड सेटिंग"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ऑडियो-वीडियो से अपने-आप कैप्शन बनना"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"बंद करें"</string>
<string name="sound_settings" msgid="8874581353127418308">"आवाज़ और वाइब्रेशन"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"सेटिंग"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"लाइव कैप्शन"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"आवाज़ को कम करके, सुरक्षित लेवल पर सेट कर दिया गया है"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"हेडफ़ोन की आवाज़ सुझाए गए समय से देर तक ज़्यादा रही"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"इस हफ़्ते के लिए हेडफ़ोन की आवाज़, सुझाई गई सीमा से ज़्यादा हो गई है"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"वाइब्रेशन की सुविधा चालू करें"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s की आवाज़ कम या ज़्यादा करने की सुविधा"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"कॉल और सूचनाएं आने पर घंटी बजेगी (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> को चलाया जा रहा है"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ऑडियो इसमें चलेगा"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"सिस्टम यूज़र इंटरफ़ेस (यूआई) ट्यूनर"</string>
<string name="status_bar" msgid="4357390266055077437">"स्टेटस बार"</string>
<string name="demo_mode" msgid="263484519766901593">"सिस्टम यूज़र इंटरफ़ेस (यूआई) डेमो मोड"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 3dc70879f4a4..d4e460ddf9ca 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Spremljeno"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekini vezu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviraj"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski ponovo uključi sutra"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Značajke kao što su brzo dijeljenje, Pronađi moj uređaj i lokacija uređaja upotrebljavaju Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Ovim uređajem upravlja tvoj roditelj. Tvoj roditelj može vidjeti podatke kao što su aplikacije kojima se koristiš, lokaciju i vrijeme upotrebe te upravljati njima."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Otključano održava TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Zaštita od krađe\nUređaj zaključan, previše pokušaja otključavanja"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Postavke zvuka"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatski titlovi za medije"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Kontrole glasnoće – %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Telefon će zvoniti za pozive i obavijesti (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Reproducira se – <xliff:g id="LABEL">%s</xliff:g>"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk će se reproducirati"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Ugađanje korisničkog sučelja sustava"</string>
<string name="status_bar" msgid="4357390266055077437">"Traka statusa"</string>
<string name="demo_mode" msgid="263484519766901593">"Demo način korisničkog sučelja sustava"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 9b37701f6d84..7c62c7a813e8 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Mentve"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"leválasztás"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiválás"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatikus visszakapcsolás holnap"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Az olyan funkciók, mint a Quick Share, a Készülékkereső és az eszköz helyadatai Bluetootht használnak"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkumulátor: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hang"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Az eszközt a szülőd felügyeli. A szülőd megtekintheti és kezelheti például a használt alkalmazásokra, a tartózkodási helyre és a képernyőidőre vonatkozó adatokat."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Feloldva tartva TrustAgent által"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Lopásgátlás\nTúl sok feloldási kísérlet, eszköz zárolva"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Hangbeállítások"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatikus feliratozás"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rezgés"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s hangerőszabályzók"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"A hívásoknál és értesítéseknél csörög a telefon (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> lejátszása itt:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Hang lejátszása itt:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Kezelőfelület-hangoló"</string>
<string name="status_bar" msgid="4357390266055077437">"Állapotsor"</string>
<string name="demo_mode" msgid="263484519766901593">"A rendszer kezelőfelületének demómódja"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index db2b3ce6ca05..8427fc3b683f 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Պահված է"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"անջատել"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ակտիվացնել"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Վաղը նորից ավտոմատ միացնել"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Գործառույթները, ինչպիսիք են Quick Share-ը, «Գտնել իմ սարքը» գործառույթը և սարքի տեղորոշումը, օգտագործում են Bluetooth-ը"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Աուդիո"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ականջակալ"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Այս սարքը կառավարում է ձեր ծնողը։ Նա կարող է դիտել և փոփոխել որոշակի տեղեկություններ, օրինակ՝ հավելվածները, որոնք դուք օգտագործում եք, ձեր տեղադրությունը և սարքի օգտագործման ժամանակը։"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ապակողպվում է TrustAgent-ի միջոցով"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Գողությունից պաշտպանություն\nՍարքը կողպվել է, ապակողպման շատ փորձեր"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ձայնի կարգավորումներ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Ավտոմատ ավելացնել ենթագրեր"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"անջատել"</string>
<string name="sound_settings" msgid="8874581353127418308">"Ձայն և թրթռոց"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Կարգավորումներ"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Կենդանի ենթագրեր"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Ձայնն իջեցվեց անվտանգ մակարդակի"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Ձայնը բարձր է եղել առաջարկված ժամանակահատվածից ավելի երկար"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Ականջակալների ձայնի ուժգնությունը այս շաբաթ գերազանցել է անվտանգ մակարդակի շեմը"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"միացնել թրթռոցը"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Ձայնի ուժգնության կառավարներ` %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Զանգերի և ծանուցումների համար հեռախոսի ձայնը միացված է (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>. նվագարկվում է"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Աուդիոն կնվագարկվի"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Համակարգի ՕՄ-ի կարգավորիչ"</string>
<string name="status_bar" msgid="4357390266055077437">"Կարգավիճակի գոտի"</string>
<string name="demo_mode" msgid="263484519766901593">"Համակարգի միջերեսի ցուցադրական ռեժիմ"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 44f9bfc3705b..a2f05b95026f 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan koneksi"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Otomatis aktifkan lagi besok"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Fitur seperti Quick Share, Temukan Perangkat Saya, dan lokasi perangkat menggunakan Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Perangkat ini dikelola oleh orang tuamu. Orang tuamu bisa melihat dan mengelola berbagai informasi, seperti aplikasi yang kamu gunakan, lokasimu, dan lama pemakaian perangkat."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Tetap terbuka kuncinya oleh TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Perlindungan pencurian\nPerangkat dikunci, terlalu banyak upaya pembukaan kunci"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Setelan suara"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Otomatis beri teks di media"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"nonaktifkan"</string>
<string name="sound_settings" msgid="8874581353127418308">"Suara &amp; getaran"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Setelan"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Teks Otomatis"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume diturunkan ke level yang lebih aman"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Volume headphone tinggi untuk waktu lebih lama dari yang direkomendasikan"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Volume headphone telah melampaui batas aman untuk minggu ini"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s kontrol volume"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Panggilan telepon dan notifikasi akan berdering (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Memutar <xliff:g id="LABEL">%s</xliff:g> di"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio akan diputar di"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Penyetel Antarmuka Pengguna Sistem"</string>
<string name="status_bar" msgid="4357390266055077437">"Bilah status"</string>
<string name="demo_mode" msgid="263484519766901593">"Mode demo UI sistem"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 8cfe4e834304..9c9a29f513fc 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Vistað"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"aftengja"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"virkja"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Kveikja sjálfkrafa aftur á morgun"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Eiginleikar á borð við flýtideilingu, „Finna tækið mitt“ og staðsetningu tækis nota Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> rafhlöðuhleðsla"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hljóð"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Höfuðtól"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Foreldri þitt stjórnar þessu tæki. Foreldri þitt getur séð og stjórnað upplýsingum eins og forritunum sem þú notar, staðsetningu þinni og skjátímanum."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Haldið opnu af TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Þjófavörn\nTækinu var læst vegna of margra tilrauna til að taka það úr lás"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Hljóðstillingar"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sjálfvirkir skjátextar"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"slökkva"</string>
<string name="sound_settings" msgid="8874581353127418308">"Hljóð og titringur"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Stillingar"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Skjátextar í rauntíma"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Hljóð lækkað í öruggari hljóðstyrk"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Hljóðstyrkur í heyrnartólum hefur verið hár í lengri tíma en mælt er með"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Hljóðstyrkur í heyrnartólum hefur náð öryggismörkum fyrir þessa viku"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titringur"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s stýringar á hljóstyrk"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Símhringingar og tilkynningar heyrast (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Í spilun í <xliff:g id="LABEL">%s</xliff:g>"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Hljóð heldur áfram að spilast"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Fínstillingar kerfisviðmóts"</string>
<string name="status_bar" msgid="4357390266055077437">"Stöðustika"</string>
<string name="demo_mode" msgid="263484519766901593">"Prufustilling kerfisviðmóts"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 268925f629ad..ec58af8fe1af 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Dispositivo salvato"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnetti"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"attiva"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Riattiva automaticamente domani"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funzionalità come Quick Share, Trova il mio dispositivo e la posizione del dispositivo usano il Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auricolare"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Questo dispositivo è gestito da uno dei tuoi genitori, il quale può visualizzare e gestire informazioni come le app che usi, la tua posizione e il tuo tempo di utilizzo."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Sbloccato da TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protezione da furti\nDisp. bloccato, troppi tentat. di sblocco"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Impostazioni audio"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sottotitoli automatici"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrazione"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controlli del volume %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"La suoneria sarà attiva per chiamate e notifiche (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> in riproduzione su"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio riprodotto su:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Ottimizzatore UI di sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra di stato"</string>
<string name="demo_mode" msgid="263484519766901593">"Modalità demo dell\'interfaccia utente di sistema"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 2b8e153402b9..c9a71c71867b 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"נשמר"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ניתוק"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"הפעלה"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"החיבור יופעל שוב אוטומטית מחר"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"‏תכונות כמו \'שיתוף מהיר\', \'איפה המכשיר שלי\' ומיקום המכשיר משתמשות בחיבור Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> סוללה"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"אודיו"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"אוזניות"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"מכשיר זה מנוהל על ידי ההורה שלך. להורה שלך יש אפשרות לצפות בפרטים כמו האפליקציות שבשימוש, המיקום וזמן המסך שלך, ולנהל אותם."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"הנעילה נמנעת על ידי סביבה מהימנה"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"הגנה מפני גניבה\nהמכשיר ננעל, יותר מדי ניסיונות לביטול הנעילה"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>‏. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"הגדרות צליל"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"הוספת כתוביות באופן אוטומטי למדיה"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"השבתה"</string>
<string name="sound_settings" msgid="8874581353127418308">"צליל ורטט"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"הגדרות"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"כתוביות מיידיות"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"עוצמת הקול הוחלשה לרמה בטוחה יותר"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"עוצמת הקול של האוזניות הייתה גבוהה במשך יותר זמן מהמומלץ"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"עוצמת הקול של האוזניות חרגה ממגבלת הבטיחות לשבוע הזה"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"רטט"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"‏בקרי עוצמת קול של %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"הטלפון יצלצל כשמתקבלות שיחות והתראות (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"הפעלה של <xliff:g id="LABEL">%s</xliff:g> במכשיר"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"האודיו יופעל במכשיר"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"שורת סטטוס"</string>
<string name="demo_mode" msgid="263484519766901593">"מצב הדגמה בממשק המשתמש של המערכת"</string>
@@ -792,7 +792,7 @@
<string name="right_keycode" msgid="2480715509844798438">"קוד מפתח ימני"</string>
<string name="left_icon" msgid="5036278531966897006">"סמל שמאלי"</string>
<string name="right_icon" msgid="1103955040645237425">"סמל ימני"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"יש ללחוץ ולגרור כדי להוסיף כרטיסי מידע"</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"יש ללחוץ ולגרור כדי להוסיף לחצנים"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"יש ללחוץ ולגרור כדי לסדר מחדש את כרטיסי המידע"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"אפשר לגרור לכאן כדי להסיר"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"יש צורך ב-<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> אריחים לפחות"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 5b42737d2ab5..dc33b0ddc15d 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"このデバイスは保護者によって管理されています。保護者は、あなたが使用するアプリ、あなたの現在地、デバイスの利用時間などの情報を確認したり、管理したりできます。"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"信頼エージェントがロック解除を管理"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"盗難防止\nデバイスをロック - ロック解除に繰り返し失敗"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>。<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"音声の設定"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"メディアの自動字幕起こし"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"バイブレーション"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s の音量調節"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"着信音と通知音が鳴ります(<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> を再生:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"音声の再生形式:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"システムUI調整ツール"</string>
<string name="status_bar" msgid="4357390266055077437">"ステータスバー"</string>
<string name="demo_mode" msgid="263484519766901593">"システム UI デモモード"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index fef2fe11016f..d78bc2072734 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"შენახული"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"კავშირის გაწყვეტა"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"გააქტიურება"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ხელახლა ავტომატურად ჩართვა ხვალ"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ფუნქციები, როგორებიცაა „სწრაფი გაზიარება“, „ჩემი მოწყობილობის პოვნა“ და „მოწყობილობის მდებარეობა“ იყენებენ Bluetooth-ს"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ბატარეა"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"აუდიო"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ყურსაცვამი"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ამ მოწყობილობას თქვენი მშობელი მართავს. თქვენი მშობელი ხედავს და მართავს ისეთ ინფორმაციას, როგორიც არის თქვენ მიერ გამოყენებული აპები, თქვენი მდებარეობა და თქვენ მიერ ეკრანთან გატარებული დრო."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"განბლოკილია TrustAgent-ის მიერ"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"მოპარვისაგან დაცვა\nდაიბლოკა განბლოკვის ბევრი მცდელობის გამო."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ხმის პარამეტრები"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"მედიის ავტომ. სუბტიტრირება"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ვიბრაცია"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s-ის ხმის მართვის საშუალებები"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"ზარებისა და შეტყობინებების მიღებისას დაირეკება (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"უკრავს <xliff:g id="LABEL">%s</xliff:g>:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"აუდიო დაიკვრება"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"სისტემის UI ტუნერი"</string>
<string name="status_bar" msgid="4357390266055077437">"სტატუსის ზოლი"</string>
<string name="demo_mode" msgid="263484519766901593">"სისტემის UI-ს დემო-რეჟიმი"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index cf5a560870b6..407da0b9b8fa 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сақталды"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажырату"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"іске қосу"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ертең автоматты түрде қосылсын"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Quick Share, Find My Device сияқты функциялар мен құрылғы локациясы Bluetooth пайдаланады."</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батарея деңгейі: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Aудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Бұл құрылғыны ата-анаңыз басқарады. Ата-анаңыз сіз пайдаланатын қолданбалар, геодерегіңіз және пайдалану уақытыңыз сияқты ақпаратты көре және басқара алады."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent арқылы құлпы ашылды."</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Ұрлықтан қорғау\nҚұрылғы құлыпталған, құлыпты ашуға тым көп әрекет жасалды."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Дыбыс параметрлері"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматты субтитр қосу"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"өшіру"</string>
<string name="sound_settings" msgid="8874581353127418308">"Дыбыс және діріл"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Параметрлер"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Live Caption"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Дыбыс қауіпсіз деңгейге түсірілді"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Құлақаспаптың жоғары дыбыс деңгейі ұсынылған уақыттан ұзақ қосылып тұрды."</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Құлақаспаптың дыбыс деңгейі осы аптадағы қауіпсіз шектен асып кетті."</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дірілдету"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Дыбысты басқару элементтері: %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Қоңыраулар мен хабарландырулар дыбысы қосулы (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> ойнатылатын құрылғы:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудио ойнатылатын құрылғы:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Жүйелік пайдаланушылық интерфейс тюнері"</string>
<string name="status_bar" msgid="4357390266055077437">"Күйін көрсету жолағы"</string>
<string name="demo_mode" msgid="263484519766901593">"Жүйе интерфейсінің демо режимі"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 480a0e5d2d8d..eaca65732975 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"បាន​រក្សាទុក"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ផ្ដាច់"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"បើកដំណើរការ"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"បើកដោយស្វ័យប្រវត្តិម្ដងទៀតនៅថ្ងៃស្អែក"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"មុខងារដូចជា Quick Share, រកឧបករណ៍របស់ខ្ញុំ និងប៊្លូធូសប្រើប្រាស់ទីតាំងឧបករណ៍"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"សំឡេង"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"កាស"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ឧបករណ៍​នេះ​ស្ថិត​ក្រោម​ការ​គ្រប់គ្រង​របស់មាតាបិតាអ្នក។ មាតាបិតារបស់អ្នកអាចមើល និងគ្រប់គ្រងព័ត៌មាន​ដូចជា កម្មវិធីដែលអ្នកប្រើ ទីតាំងរបស់អ្នក និងរយៈពេលប្រើប្រាស់ឧបករណ៍របស់អ្នកជាដើម។"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"បាន​ដោះសោ​ដោយភ្នាក់ងារ​​ទុកចិត្ត"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"ការការពារ​ចោរ​លួច\nបានចាក់សោឧបករណ៍ ការព្យាយាមដោះសោច្រើនដងពេក"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ការកំណត់សំឡេង"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ដាក់អក្សររត់លើមេឌៀដោយស្វ័យប្រវត្តិ"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ញ័រ"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s របារ​បញ្ជា​កម្រិត​សំឡេង"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"ការ​ហៅ​ទូរសព្ទ និង​ការជូន​ដំណឹង​នឹង​រោទ៍ (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"កំពុងចាក់​​ <xliff:g id="LABEL">%s</xliff:g> នៅ​លើ"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"សំឡេងនឹងលេងនៅលើ"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"កម្មវិធីសម្រួល UI ប្រព័ន្ធ"</string>
<string name="status_bar" msgid="4357390266055077437">"របារស្ថានភាព"</string>
<string name="demo_mode" msgid="263484519766901593">"មុខងារ​សាកល្បង​ UI ប្រព័ន្ធ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 1f9156afc073..64179ba03a3a 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ಸೇವ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ಡಿಸ್‌ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ನಾಳೆ ಪುನಃ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಮಾಡಿ"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ಕ್ವಿಕ್ ಶೇರ್, Find My Device ನಂತಹ ಫೀಚರ್‌ಗಳು ಮತ್ತು ಸಾಧನದ ಸ್ಥಳ ಬ್ಲೂಟೂತ್ ಬಳಸುತ್ತವೆ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ಆಡಿಯೋ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ಹೆಡ್‌ಸೆಟ್"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ಈ ಸಾಧನವನ್ನು ನಿಮ್ಮ ಪೋಷಕರು ನಿರ್ವಹಿಸುತ್ತಿದ್ದಾರೆ. ನೀವು ಬಳಸುವ ಆ್ಯಪ್‌ಗಳು, ನಿಮ್ಮ ಸ್ಥಳ ಮತ್ತು ನಿಮ್ಮ ವೀಕ್ಷಣಾ ಅವಧಿಯಂತಹ ಮಾಹಿತಿಯನ್ನು ನಿಮ್ಮ ಪೋಷಕರು ನೋಡಬಹುದು ಮತ್ತು ನಿರ್ವಹಿಸಬಹುದು."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ನಿಂದ ಅನ್‌ಲಾಕ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"ಕಳ್ಳತನದ ರಕ್ಷಣೆ\nಸಾಧನ ಲಾಕ್ ಆಗಿದೆ, ಅನ್‌ಲಾಕ್‌ಗೆ ಹೆಚ್ಚು ಪ್ರಯತ್ನ..."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ಸೌಂಡ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ಸ್ವಯಂಚಾಲಿತ ಶೀರ್ಷಿಕೆ ಮಾಧ್ಯಮ"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ವೈಬ್ರೇಟ್‌"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ವಾಲ್ಯೂಮ್ ನಿಯಂತ್ರಕಗಳು"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"(<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>) ನಲ್ಲಿ ಕರೆಗಳು ಮತ್ತು ಅಧಿಸೂಚನೆಗಳು ರಿಂಗ್ ಆಗುತ್ತವೆ"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಆಗು..."</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ಇಲ್ಲಿ ಆಡಿಯೋ ಪ್ಲೇ..."</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"ಸಿಸ್ಟಂ UI ಟ್ಯೂನರ್"</string>
<string name="status_bar" msgid="4357390266055077437">"ಸ್ಥಿತಿ ಪಟ್ಟಿ"</string>
<string name="demo_mode" msgid="263484519766901593">"ಸಿಸ್ಟಂ UI ಡೆಮೋ ಮೋಡ್"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index c0c8b32237f7..26825e0286e3 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"저장됨"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"연결 해제"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"실행"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"내일 다시 자동으로 사용 설정"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Quick Share, 내 기기 찾기, 기기 위치 등의 기능에서 블루투스를 사용합니다."</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"오디오"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"헤드셋"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"부모님이 관리하는 기기입니다. 부모님이 내가 사용하는 앱, 내 위치, 기기 사용 시간과 같은 정보를 보고 관리할 수 있습니다."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent가 잠금 해제함"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"도난 방지\n기기 잠김, 잠금 해제 시도 횟수가 너무 많음"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"소리 설정"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"미디어 자막 자동 생성"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"사용 중지"</string>
<string name="sound_settings" msgid="8874581353127418308">"소리 및 진동"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"설정"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"실시간 자막"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"볼륨을 안전한 수준으로 낮춤"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"헤드폰 볼륨이 권장 시간보다 오래 높은 상태였습니다."</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"헤드폰 볼륨이 이번 주 안전 한도를 초과했습니다."</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"진동"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s 볼륨 컨트롤"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"전화 및 알림이 오면 벨소리가 울림(<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> 재생 위치:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"오디오 재생 위치:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"시스템 UI 튜너"</string>
<string name="status_bar" msgid="4357390266055077437">"상태 표시줄"</string>
<string name="demo_mode" msgid="263484519766901593">"시스템 UI 데모 모드"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index b442f2ea255f..79546862454b 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сакталды"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажыратуу"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"иштетүү"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Эртең автоматтык түрдө кайра күйгүзүү"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Тез Бөлүшүү, \"Түзмөгүм кайда?\" жана түзмөктүн турган жерин аныктоо сыяктуу функциялар Bluetooth\'ду колдонот"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Бул түзмөктү ата-энең башкарат. Ата-энең сен иштеткен колдонмолорду, кайда жүргөнүңдү жана түзмөктү канча убакыт колдонгонуңду көрүп, башкарып турат."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ишеним агенти кулпусун ачты"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Түзмөктүн уурдалышынан коргоо\nТүзмөк кулпуланды. Кулпуну ачууга өтө көп аракет жасалды"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Добуштун параметрлери"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматтык коштомо жазуулар"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"өчүрүү"</string>
<string name="sound_settings" msgid="8874581353127418308">"Үн жана дирилдөө"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Параметрлер"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Ыкчам коштомо жазуулар"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Үндүн катуулугу коопсуз деңгээлге чейин акырындатылды"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Гарнитуранын үнүн катуу чыгарып, сунушталгандан узагыраак угуп жатасыз"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Гарнитуранын үнүнүн катуулугу бул аптада коопсуз деңгээлден жогору болду"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дирилдөө"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s үндү башкаруу элементтери"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Чалуулар менен эскертмелердин үнү чыгарылат (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> аркылуу ойнотулууда"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудио төмөнкүдө ойнотулат:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Абал тилкеси"</string>
<string name="demo_mode" msgid="263484519766901593">"Системанын интерфейсинин демо режими"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index ee170ce44f8e..4c1bd4eb0d14 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ອຸປະກອນນີ້ແມ່ນຈັດການໂດຍພໍ່ແມ່ຂອງທ່ານ. ພໍ່ແມ່ຂອງທ່ານສາມາດເບິ່ງ ແລະ ຈັດການຂໍ້ມູນໄດ້ ເຊັ່ນ: ແອັບທີ່ທ່ານໃຊ້, ສະຖານທີ່ ແລະ ເວລາໜ້າຈໍຂອງທ່ານ."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"ປັອດລັອກປະໄວ້ໂດຍ TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"ການປ້ອງກັນການຖືກລັກ\nຂອງອຸປະກອນຖືກລັອກ, ພະຍາຍາມປົດລັອກຫຼາຍເທື່ອເກີນໄປ"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ການຕັ້ງຄ່າສຽງ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ສ້າງຄຳບັນຍາຍມີເດຍໂດຍອັດຕະໂນມັດ"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ສັ່ນເຕືອນ"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"ການຄວບຄຸມສຽງ %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"ການໂທ ແລະ ການແຈ້ງເຕືອນຈະມີສຽງດັງ (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"ກຳລັງຫຼິ້ນ <xliff:g id="LABEL">%s</xliff:g> ໃນ"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ສຽງຈະຫຼິ້ນຕໍ່ໄປ"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"ແຖບສະຖານະ"</string>
<string name="demo_mode" msgid="263484519766901593">"ໂໝດເດໂມສ່ວນຕິດຕໍ່ຜູ້ໃຊ້ລະບົບ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 5b71259382a0..9a261e05f709 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Šį įrenginį tvarko vienas iš tavo tėvų. Jis gali peržiūrėti ir tvarkyti informaciją, pvz., tavo naudojamas programas, vietovę ir įrenginio naudojimo laiką."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Atrakinta taikant „TrustAgent“"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Apsauga nuo vagystės\nĮrenginys užrakintas, per daug bandymų atrakinti"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Garso nustatymai"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Taikyti aut. medij. subtitr."</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibruoti"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Garsumo valdikliai: %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Skambučiai ir pranešimai skambės (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Leidžiama „<xliff:g id="LABEL">%s</xliff:g>“"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Garsas bus leidžiamas"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sistemos naudotojo sąsajos derinimo priemonė"</string>
<string name="status_bar" msgid="4357390266055077437">"Būsenos juosta"</string>
<string name="demo_mode" msgid="263484519766901593">"Sistemos NS demonstracinis režimas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index b4dae4c71a95..a9a1a3c366a4 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saglabāta"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atvienot"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizēt"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automātiski atkal ieslēgt rīt"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Tādas funkcijas kā “Ātrā kopīgošana”, “Atrast ierīci” un ierīces atrašanās vietas noteikšana izmanto tehnoloģiju Bluetooth."</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumulators: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Austiņas"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Šo ierīci pārvalda viens no jūsu vecākiem. Vecāki var skatīt un pārvaldīt tādu informāciju kā jūsu izmantotās lietotnes, atrašanās vieta un izmantošanas ilgums."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Bloķēšanu liedzis TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Aizsardzība pret zādzību\nIerīce bloķēta; pārāk daudz atbloķēšanas mēģinājumu"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Skaņas iestatījumi"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Autom. paraksti multividei"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"atspējot"</string>
<string name="sound_settings" msgid="8874581353127418308">"Skaņa un vibrācija"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Iestatījumi"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Subtitri reāllaikā"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Skaļums samazināts līdz drošākam līmenim"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Austiņu skaļums ir bijis liels ilgāk, nekā ieteicams."</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Austiņu skaļums ir pārsniedzis šīs nedēļas drošo ierobežojumu."</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrēt"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s skaļuma vadīklas"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Zvani un paziņojumi aktivizēs zvana signālu (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> — atskaņošana šeit:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio tiks atskaņots šeit:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sistēmas saskarnes regulators"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusa josla"</string>
<string name="demo_mode" msgid="263484519766901593">"Sistēmas lietotāja saskarnes demonstrācijas režīms"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 38ed5a70ad3c..20a93f7df6cf 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Зачувано"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекини врска"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активирај"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматски вклучи повторно утре"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Функциите како „Брзо споделување“, „Најди го мојот уред“ и локација на уредот користат Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батерија"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Родителот управува со уредов. Родителот може да прегледува и управува со податоците, како што се апликациите што ги користиш, твојата локација и времето поминато на уредот."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"ВПН"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Се одржува отклучен од TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Заштита од кражби\nЗаклучено. Премногу обиди за отклучување."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Поставки за звукот"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматски титлови"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"оневозможи"</string>
<string name="sound_settings" msgid="8874581353127418308">"Звук и вибрации"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Поставки"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Автоматски титлови"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Јачината на звукот е намалена на побезбедно ниво"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Јачината на звукот на слушалките беше висока подолго од препорачаното"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Јачината на звукот на слушалките го надмина безбедното ограничување за седмицава"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрации"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Контроли на јачината на звукот за %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Повиците и известувањата ќе ѕвонат (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>: пуштено на"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудиото ќе се пушти на"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Адаптер на УИ на системот"</string>
<string name="status_bar" msgid="4357390266055077437">"Статусна лента"</string>
<string name="demo_mode" msgid="263484519766901593">"Демо-режим на кориснички интерфејс на систем"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index a20417da5660..fc2bc47b64dc 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ഈ ഉപകരണം മാനേജ് ചെയ്യുന്നത് നിങ്ങളുടെ രക്ഷിതാവാണ്. നിങ്ങൾ ഉപയോഗിക്കുന്ന ആപ്പുകൾ, സ്‌ക്രീൻ സമയം, ലൊക്കേഷൻ എന്നിവ പോലുള്ള വിവരങ്ങൾ നിങ്ങളുടെ രക്ഷിതാവിന് കാണാനും നിയന്ത്രിക്കാനുമാകും."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ഉപയോഗിച്ച് അൺലോക്ക് ചെയ്‌തത്"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"മോഷണ പരിരക്ഷ\nഉപകരണം ലോക്ക് ചെയ്തു, നിരവധി അൺലോക്ക് ശ്രമങ്ങൾ"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ശബ്‌ദ ക്രമീകരണം"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"മീഡിയയ്ക്ക് സ്വയമേവ ക്യാപ്ഷൻ"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"വൈബ്രേറ്റ് ചെയ്യുക"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ശബ്‌ദ നിയന്ത്രണങ്ങൾ"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"കോളുകളും അറിയിപ്പുകളും ലഭിക്കുമ്പോൾ റിംഗ് ചെയ്യും (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> എന്നതിൽ പ്ലേ ചെയ്യുന്നു"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ഓഡിയോ പ്ലേ ചെയ്യും"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"സിസ്റ്റം UI ട്യൂണർ"</string>
<string name="status_bar" msgid="4357390266055077437">"സ്റ്റാറ്റസ് ബാർ"</string>
<string name="demo_mode" msgid="263484519766901593">"സിസ്റ്റം UI ഡെമോ മോഡ്"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 378f330f5435..f5bf05a4d671 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Хадгалсан"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"салгах"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"идэвхжүүлэх"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Маргааш автоматаар дахин асаах"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Түргэн хуваалцах, Миний төхөөрөмжийг олох зэрэг онцлогууд болон төхөөрөмжийн байршил Bluetooth-г ашигладаг"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батарей"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Чихэвч"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Энэ төхөөрөмжийг таны эцэг эх удирддаг. Таны эцэг эх таны хэрэглэдэг апп, байршил, дэлгэцийн цаг зэрэг мэдээллийг харж, удирдах боломжтой."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent-р түгжээгүй байлгасан"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Хулгайн хамгаалалт\nТөхөөрөмж түгжигдсэн, түгжээг тайлах хэт олон оролдлого"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Дууны тохиргоо"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Медиад автоматаар тайлбар нэмэх"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"идэвхгүй болгох"</string>
<string name="sound_settings" msgid="8874581353127418308">"Дуу, чичиргээ"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Тохиргоо"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Шууд тайлбар"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Дууны түвшнийг илүү аюулгүй түвшин рүү багасгасан"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Чихэвчийн дууны түвшин санал болгосноос удаан хугацааны туршид өндөр байсан"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Чихэвчийн дууны түвшин энэ долоо хоногийн аюулгүй хязгаараас хэтэрсэн"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"чичрэх"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s түвшний хяналт"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Дуудлага болон мэдэгдлийн хонх дуугарна (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> дээр тоглуулж байна"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудиог дараахад тоглуулна"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Системийн UI Тохируулагч"</string>
<string name="status_bar" msgid="4357390266055077437">"Статус самбар"</string>
<string name="demo_mode" msgid="263484519766901593">"Системийн UI демо горим"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 940a7e6692e9..f9d4aca9efcd 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -148,7 +148,7 @@
<string name="accessibility_scanning_face" msgid="3093828357921541387">"चेहरा स्कॅन करत आहे"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"पाठवा"</string>
<string name="cancel" msgid="1089011503403416730">"रद्द करा"</string>
- <string name="biometric_dialog_confirm" msgid="2005978443007344895">"कंफर्म करा"</string>
+ <string name="biometric_dialog_confirm" msgid="2005978443007344895">"कन्फर्म करा"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"पुन्हा प्रयत्न करा"</string>
<string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"ऑथेंटिकेशन रद्द करण्यासाठी टॅप करा"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4351777022315116816">"कृपया पुन्हा प्रयत्न करा"</string>
@@ -205,7 +205,7 @@
<string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"फेस अनलॉक उपलब्ध नाही"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ब्लूटूथ कनेक्‍ट केले."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"ब्लूटूथ डिव्‍हाइस आयकन"</string>
- <string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"डिव्हाइसचे तपशील कॉंफिगर करण्यासाठी क्लिक करा"</string>
+ <string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"डिव्हाइसचे तपशील कॉन्फिगर करण्यासाठी क्लिक करा"</string>
<string name="accessibility_bluetooth_device_settings_see_all" msgid="9111952496905423543">"सर्व डिव्हाइस पाहण्यासाठी क्लिक करा"</string>
<string name="accessibility_bluetooth_device_settings_pair_new_device" msgid="2435184865793496966">"नवीन डिव्हाइस पेअर करण्यासाठी क्लिक करा"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"बॅटरीच्या चार्जिंगची टक्केवारी माहित नाही."</string>
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव्ह केले"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिस्कनेक्ट करा"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ॲक्टिव्हेट करा"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"उद्या पुन्हा आपोआप सुरू करा"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Quick Share, Find My Device आणि डिव्हाइस स्थान यांसारखी वैशिष्ट्ये ब्लूटूथ वापरतात"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बॅटरी"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडिओ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"हे डिव्हाइस तुमच्या पालकाने व्यवस्थापित केले आहे. तुम्ही वापरत असलेली ॲप्स, तुमचे स्थान आणि तुमचा स्क्रीन वेळ यांसारखी माहिती तुमचे पालक पाहू आणि व्यवस्‍थापित करू शकतात."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ने अनलॉक ठेवले"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"चोरीपासून संरक्षण\nडिव्हाइस लॉक केले, अनलॉक करायचे खूप प्रयत्न"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"आवाज सेटिंग्ज"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"मीडियाला आपोआप सबटायटल द्या"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"व्हायब्रेट करा"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s व्हॉल्यूम नियंत्रण"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"कॉल आणि सूचना वाजतील (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> वर प्ले करत आहे"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"यावर ऑडिओ प्ले होईल"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"सिस्टम UI ट्युनर"</string>
<string name="status_bar" msgid="4357390266055077437">"स्टेटस बार"</string>
<string name="demo_mode" msgid="263484519766901593">"सिस्टम UI डेमो मोड"</string>
@@ -647,7 +648,7 @@
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे संभाषण वैशिष्ट्यांना सपोर्ट करत नाही"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"या सूचनांमध्ये सुधारणा केली जाऊ शकत नाही."</string>
<string name="notification_unblockable_call_desc" msgid="5907328164696532169">"कॉलशी संबंधित सूचनांमध्ये फेरबदल केला जाऊ शकत नाही."</string>
- <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉंफिगर केला जाऊ शकत नाही"</string>
+ <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉन्फिगर केला जाऊ शकत नाही"</string>
<string name="notification_delegate_header" msgid="1264510071031479920">"प्रॉक्सी केलेल्या सूचना"</string>
<string name="notification_channel_dialog_title" msgid="6856514143093200019">"सर्व <xliff:g id="APP_NAME">%1$s</xliff:g> वरील सूचना"</string>
<string name="see_more_title" msgid="7409317011708185729">"आणखी पहा"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index fdffdb7265fd..dafcfe009769 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Peranti ini diurus oleh ibu bapa anda. Ibu bapa anda dapat melihat dan mengurus maklumat seperti apl yang anda gunakan, lokasi dan masa skrin anda."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Dibiarkan tidak berkunci oleh TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Perlindungan kecurian\nDikunci, banyak percubaan membuka kunci"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Tetapan bunyi"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sari kata media automatik"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s kawalan kelantangan"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Panggilan dan pemberitahuan akan berdering (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Memainkan <xliff:g id="LABEL">%s</xliff:g> pada"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio dimainkan pada"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Penala UI Sistem"</string>
<string name="status_bar" msgid="4357390266055077437">"Bar status"</string>
<string name="demo_mode" msgid="263484519766901593">"Mod tunjuk cara UI sistem"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 99e2a3fcd53c..e52b899870d2 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"သိမ်းထားသည်"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ချိတ်ဆက်မှုဖြုတ်ရန်"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"စသုံးရန်"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"မနက်ဖြန် အလိုအလျောက် ထပ်ဖွင့်ရန်"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"‘အမြန် မျှဝေပါ’၊ Find My Device နှင့် စက်ပစ္စည်းတည်နေရာကဲ့သို့ တူးလ်များသည် ဘလူးတုသ်သုံးသည်"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ဘက်ထရီ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"အသံ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"မိုက်ခွက်ပါနားကြပ်"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ဤစက်ပစ္စည်းကို သင့်မိဘက စီမံခန့်ခွဲသည်။ သင့်မိဘက သင်သုံးသောအက်ပ်များ၊ သင်၏တည်နေရာနှင့် အသုံးပြုချိန် ကဲ့သို့သော အချက်အလက်များကို မြင်နိုင်ပြီး စီမံခန့်ခွဲနိုင်သည်။"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ဖြင့် ဆက်ဖွင့်ထားရန်"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"သူခိုးကာကွယ်ရေး\nစက်လော့ခ်ကျ၊ အကြိမ်များစွာဖွင့်ရန်ကြိုးစားထား"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>။ <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"အသံဆက်တင်များ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"အလိုအလျောက် စာတန်းထိုးရန်"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"ပိတ်ရန်"</string>
<string name="sound_settings" msgid="8874581353127418308">"အသံနှင့် တုန်ခါမှု"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ဆက်တင်များ"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"တိုက်ရိုက်စာတန်း"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"အသံကို ဘေးကင်းသည့်အဆင့်သို့ လျှော့ချလိုက်သည်"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"နားကြပ်အသံသည် အကြံပြုထားသည်ထက် အချိန်ကြာရှည်စွာ ကျယ်လောင်နေသည်"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"နားကြပ်အသံသည် ဤအပတ်အတွက် ဘေးကင်းသည့်ကန့်သတ်ချက်ထက် ကျော်သွားပါပြီ"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"တုန်ခါမှု"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s အသံအတိုးအလျှော့ ခလုတ်များ"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"ခေါ်ဆိုမှုများနှင့် အကြောင်းကြားချက်များအတွက် အသံမြည်နှုန်း (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>) ဖြစ်သည်"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> ကို ဖွင့်နေသည်"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"အောက်တွင်အသံဖွင့်မည်"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"စနစ် UI ဖမ်းစက်"</string>
<string name="status_bar" msgid="4357390266055077437">"အခြေအနေပြနေရာ"</string>
<string name="demo_mode" msgid="263484519766901593">"စနစ် UI စရုပ်ပြမုဒ်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 67f44e9c72c2..ed6e2931ed5f 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Lagret"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koble fra"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiver"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Slå på igjen i morgen automatisk"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funksjoner som Quick Share, Finn enheten min og enhetsposisjon bruker Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Hodetelefoner"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Denne enheten administreres av forelderen din. Forelderen din kan se og administrere informasjon, for eksempel appene du bruker, posisjonen din og skjermtiden din."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Holdes opplåst med TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Tyveribeskyttelse\nEnheten er låst – mange opplåsingsforsøk"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Lydinnstillinger"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatisk medieteksting"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"deaktiver"</string>
<string name="sound_settings" msgid="8874581353127418308">"Lyd og vibrering"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Innstillinger"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Direkteteksting"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volumet er senket til et tryggere nivå"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Volumet på hodetelefonene har vært høyt lenger enn anbefalt"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Volumet på hodetelefonene har overskredet sikkerhetsgrensen for denne uken"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s volumkontroller"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Anrop og varsler ringer (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Spiller av <xliff:g id="LABEL">%s</xliff:g> på"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Lyden spilles av på"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusrad"</string>
<string name="demo_mode" msgid="263484519766901593">"Demomodus for systemgrensesnitt"</string>
@@ -792,7 +792,7 @@
<string name="right_keycode" msgid="2480715509844798438">"Høyre-tastkode"</string>
<string name="left_icon" msgid="5036278531966897006">"Venstre-ikon"</string>
<string name="right_icon" msgid="1103955040645237425">"Høyre-ikon"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"Hold og dra for å legge til ruter"</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"Hold og dra for å legge til brikker"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Hold og dra for å flytte på rutene"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Dra hit for å fjerne"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Du trenger minst <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> infobrikker"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index f9fc6a65edb4..fb7680daf152 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेभ गरिएको छ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिस्कनेक्ट गर्नुहोस्"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"एक्टिभेट गर्नुहोस्"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"भोलि फेरि स्वतः अन गरियोस्"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"क्विक सेयर, Find My Device र डिभाइसको लोकेसन जस्ता सुविधाहरूले ब्लुटुथ प्रयोग गर्छन्"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ब्याट्री"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"अडियो"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"यो डिभाइस तपाईंका अभिभावक व्यवस्थापन गर्नुहुन्छ। तपाईंका अभिभावक तपाईंले प्रयोग गर्ने एप, तपाईंको स्थान र तपाईंले यन्त्र चलाएर बिताउने समय जस्ता जानकारी हेर्न तथा व्यवस्थापन गर्न सक्नुहुन्छ।"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ले खुला राखेको"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"चोरीबाट सुरक्षा\nडिभाइस लक गरिएको छ, अत्यधिक धेरै पटक अनलक गर्ने प्रयास गरिएको छ"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ध्वनिसम्बन्धी सेटिङहरू"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"मिडियाको स्वत: क्याप्सन बनाउनुहोस्"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"कम्पन गर्नुहोस्"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s भोल्युमका नियन्त्रणहरू"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"कल तथा सूचनाहरू आउँदा घन्टी बज्ने छ (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> प्ले गरिँदै छ"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"अडियो प्ले भइरहने छ"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"सिस्टम UI ट्युनर"</string>
<string name="status_bar" msgid="4357390266055077437">"स्थिति पट्टी"</string>
<string name="demo_mode" msgid="263484519766901593">"सिस्टम UI को डेमो मोड"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 668dd0eea310..a649617a5559 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Dit apparaat wordt beheerd door je ouder. Je ouder kan informatie bekijken en beheren, zoals de apps die je gebruikt, je locatie en je schermtijd."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ontgrendeld gehouden door TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Diefstalbeveiliging\nApparaat vergrendeld, te veel ontgrendelpogingen"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Geluidsinstellingen"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatisch ondertitelen"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"trillen"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s-volumeknoppen"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Geluid bij gesprekken en meldingen (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> wordt afgespeeld op"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio wordt afgespeeld op"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Systeem-UI-tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusbalk"</string>
<string name="demo_mode" msgid="263484519766901593">"Demomodus voor systeemgebruikersinterface"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index ee9a5fd3c721..b0f5022c9c17 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ସେଭ କରାଯାଇଛି"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ଡିସକନେକ୍ଟ କରନ୍ତୁ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ଚାଲୁ କରନ୍ତୁ"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ଆସନ୍ତାକାଲି ସ୍ୱତଃ ପୁଣି ଚାଲୁ ହେବ"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Quick Share, Find My Device ଏବଂ ଡିଭାଇସ ଲୋକେସନ ପରି ଫିଚରଗୁଡ଼ିକ ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରେ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ବ୍ୟାଟେରୀ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ଅଡିଓ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ହେଡସେଟ୍‍"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ଏହି ଡିଭାଇସ୍ ଆପଣଙ୍କ ବାପାମାଙ୍କ ଦ୍ୱାରା ପରିଚାଳିତ। ଆପଣଙ୍କ ବାପାମା ଆପଣ ବ୍ୟବହାର କରୁଥିବା ଆପ୍ସ, ଆପଣଙ୍କ ଲୋକେସନ୍ ଓ ସ୍କ୍ରିନ୍ ସମୟ ପରି ସୂଚନା ଦେଖିପାରିବେ ଏବଂ ପରିଚାଳନା କରିପାରିବେ।"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ଦ୍ୱାରା ଅନ୍‌ଲକ୍ ରହିଛି"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"ଥେଫ୍ଟ ସୁରକ୍ଷା\nଡିଭାଇସ ଲକ କରାଯାଇଛି, ଅନେକଗୁଡ଼ିଏ ଅନଲକ ପ୍ରଚେଷ୍ଟା"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ସାଉଣ୍ଡ ସେଟିଂସ୍"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ସ୍ବଚାଳିତ କ୍ୟାପ୍ସନ୍ ମିଡିଆ"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"ଅକ୍ଷମ କରନ୍ତୁ"</string>
<string name="sound_settings" msgid="8874581353127418308">"ସାଉଣ୍ଡ ଓ ଭାଇବ୍ରେସନ"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ସେଟିଂସ"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"ଲାଇଭ କେପ୍ସନ"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"ଭଲ୍ୟୁମକୁ ସୁରକ୍ଷିତ ଲେଭେଲକୁ କମ କରାଯାଇଛି"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"ସୁପାରିଶ ଭଲ୍ୟୁମ ଠାରୁ ହେଡଫୋନର ଭଲ୍ୟୁମ ଅଧିକ ଅଛି"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"ଏହି ସପ୍ତାହ ପାଇଁ ହେଡଫୋନର ଭଲ୍ୟୁମ ସୁରକ୍ଷିତ ସୀମାକୁ ଅତିକ୍ରମ କରିଛି"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ଭାଇବ୍ରେଟ୍"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ଭଲ୍ୟୁମ୍ ନିୟନ୍ତ୍ରଣ"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"କଲ୍ ଓ ବିଜ୍ଞପ୍ତି ପାଇଁ (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)ରେ ରିଙ୍ଗ ହେବ"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>ରେ ପ୍ଲେ କରାଯାଉଛି"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ଅଡିଓ ପ୍ଲେ ହେବ"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"ସିଷ୍ଟମ୍ UI ଟ୍ୟୁନର୍‍"</string>
<string name="status_bar" msgid="4357390266055077437">"ଷ୍ଟାଟସ୍‍ ବାର୍‍"</string>
<string name="demo_mode" msgid="263484519766901593">"ସିଷ୍ଟମ୍‌ UI ଡେମୋ ମୋଡ୍‌"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index f81b3b49cc81..6916fd2b3be9 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ਕਿਰਿਆਸ਼ੀਲ ਕਰੋ"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ਕੱਲ੍ਹ ਨੂੰ ਆਪਣੇ ਆਪ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ਕਵਿੱਕ ਸ਼ੇਅਰ, Find My Device ਅਤੇ ਡੀਵਾਈਸ ਦਾ ਟਿਕਾਣਾ ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਬਲੂਟੁੱਥ ਦੀ ਵਰਤੋਂ ਕਰਦੀਆਂ ਹਨ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ਆਡੀਓ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ਹੈੱਡਸੈੱਟ"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਤੁਹਾਡੇ ਮਾਂ-ਪਿਓ ਵੱਲੋਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਤੁਹਾਡੇ ਮਾਂ-ਪਿਓ ਤੁਹਾਡੀਆਂ ਐਪਾਂ ਦੀ ਵਰਤੋਂ, ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਅਤੇ ਤੁਹਾਡੇ ਸਕ੍ਰੀਨ ਸਮੇਂ ਵਰਗੀ ਜਾਣਕਾਰੀ ਨੂੰ ਦੇਖ ਅਤੇ ਉਸਦਾ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦੇ ਹਨ।"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"ਟਰੱਸਟ-ਏਜੰਟ ਵੱਲੋਂ ਅਣਲਾਕ ਰੱਖਿਆ ਗਿਆ"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"ਚੋਰੀ ਤੋਂ ਸੁਰੱਖਿਆ\nਡੀਵਾਈਸ ਲਾਕ ਹੋ ਗਿਆ, ਅਣਲਾਕ ਕਰਨ ਦੀਆਂ ਕਈ ਕੋਸ਼ਿਸ਼ਾਂ ਕੀਤੀਆਂ ਗਈਆਂ"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ਧੁਨੀ ਸੈਟਿੰਗਾਂ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ਸਵੈਚਲਿਤ ਸੁਰਖੀ ਮੀਡੀਆ"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"ਬੰਦ ਕਰੋ"</string>
<string name="sound_settings" msgid="8874581353127418308">"ਧੁਨੀ ਅਤੇ ਥਰਥਰਾਹਟ"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ਸੈਟਿੰਗਾਂ"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"ਲਾਈਵ ਸੁਰਖੀਆਂ"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"ਅਵਾਜ਼ ਨੂੰ ਜ਼ਿਆਦਾ ਸੁਰੱਖਿਅਤ ਪੱਧਰ ਤੱਕ ਘੱਟ ਕੀਤਾ ਗਿਆ"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"ਹੈੱਡਫ਼ੋਨ ਦੀ ਅਵਾਜ਼ ਸਿਫ਼ਾਰਸ਼ੀ ਪੱਧਰ ਨਾਲੋਂ ਜ਼ਿਆਦਾ ਲੰਬੇ ਸਮੇਂ ਤੱਕ ਉੱਚੀ ਰਹੀ"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"ਹੈੱਡਫ਼ੋਨ ਦੀ ਅਵਾਜ਼ ਇਸ ਹਫ਼ਤੇ ਦੀ ਸੁਰੱਖਿਅਤ ਸੀਮਾ ਨੂੰ ਪਾਰ ਕਰ ਗਈ"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ਥਰਥਰਾਹਟ"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ਵੌਲਿਊਮ ਕੰਟਰੋਲ"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"ਕਾਲਾਂ ਆਉਣ ਅਤੇ ਸੂਚਨਾਵਾਂ ਮਿਲਣ \'ਤੇ ਘੰਟੀ ਵਜੇਗੀ (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> ਚਲਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ਆਡੀਓ ਇਸ \'ਤੇ ਚੱਲੇਗੀ"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI ਟਿਊਨਰ"</string>
<string name="status_bar" msgid="4357390266055077437">"ਸਥਿਤੀ ਪੱਟੀ"</string>
<string name="demo_mode" msgid="263484519766901593">"ਸਿਸਟਮ UI ਡੈਮੋ ਮੋਡ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index a22d77c17b2f..c04d6dae6e0d 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Zapisane"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"rozłącz"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktywuj"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatycznie włącz ponownie jutro"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funkcje takie jak Szybkie udostępnianie, Znajdź moje urządzenie i dotyczące lokalizacji urządzenia używają Bluetootha"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> naładowania baterii"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Dźwięk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Zestaw słuchawkowy"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Tym urządzeniem zarządza Twój rodzic. Rodzic może zobaczyć różne informacje, np. o aplikacjach, których używasz, lokalizacji i czasie korzystania z urządzenia, a także zarządzać tymi danymi."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Blokada anulowana przez agenta zaufania"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Ochrona przed kradzieżą\nZablokowano – za dużo prób logowania."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ustawienia dźwięku"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Autom. napisy do multimediów"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"wyłącz"</string>
<string name="sound_settings" msgid="8874581353127418308">"Dźwięk i wibracje"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Ustawienia"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Napisy na żywo"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Głośność obniżona do bezpieczniejszego poziomu"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Głośność na słuchawkach jest zbyt duża przez czas dłuższy niż zalecany"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Głośność na słuchawkach przekroczyła limit bezpieczeństwa na ten tydzień"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"włącz wibracje"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Sterowanie głośnością: %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Połączenia i powiadomienia będą uruchamiały dzwonek (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Odtwarzam <xliff:g id="LABEL">%s</xliff:g> na"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Wyjścia dźwięku:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Kalibrator System UI"</string>
<string name="status_bar" msgid="4357390266055077437">"Pasek stanu"</string>
<string name="demo_mode" msgid="263484519766901593">"Tryb demonstracyjny interfejsu"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index c4e2d3fd063e..e82a1032dbc6 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Recursos como o Quick Share, o Encontre Meu Dispositivo e a localização do dispositivo usam o Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Este dispositivo é gerenciado pelo seu familiar responsável, que pode ver e gerenciar informações como os apps que você usa, sua localização e seu tempo de uso."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloqueado pelo TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Proteção contra roubo\nDispositivo bloqueado por tentativas de desbloqueio em excesso"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Configurações de som"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Transcrição automática"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"desativar"</string>
<string name="sound_settings" msgid="8874581353127418308">"Som e vibração"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Configurações"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Legenda instantânea"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume diminuído para um nível mais seguro"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"O volume dos fones de ouvido está alto há mais tempo que o recomendado"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"O volume dos fones de ouvido excedeu o limite de segurança para esta semana"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controles de volume %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Chamadas e notificações farão o smartphone tocar (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"O áudio vai tocar em"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra de status"</string>
<string name="demo_mode" msgid="263484519766901593">"Modo de demonstração da interface do sistema"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 267c8cd64bb8..8ba907b4ee41 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desassociar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Reativar amanhã automaticamente"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"As funcionalidades como Partilha rápida, Localizar o meu dispositivo e localização do dispositivo usam o Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ausc. c/ mic. integ."</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Este dispositivo é gerido pelos teus pais, que podem ver e gerir informações como as apps que utilizas, a tua localização e o tempo de utilização."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Mantido desbloqueado pelo TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Proteção contra roubo\nDisp. bloq., muitas tentativas de desb."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Definições de som"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Multim. legendas automáticas"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controlos de volume de %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"As chamadas e as notificações tocam (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"A ouvir <xliff:g id="LABEL">%s</xliff:g> em"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"O áudio será ouv. em"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador da interface do sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra de estado"</string>
<string name="demo_mode" msgid="263484519766901593">"Modo de demonstração da IU do sistema"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index c4e2d3fd063e..e82a1032dbc6 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Recursos como o Quick Share, o Encontre Meu Dispositivo e a localização do dispositivo usam o Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Este dispositivo é gerenciado pelo seu familiar responsável, que pode ver e gerenciar informações como os apps que você usa, sua localização e seu tempo de uso."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloqueado pelo TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Proteção contra roubo\nDispositivo bloqueado por tentativas de desbloqueio em excesso"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Configurações de som"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Transcrição automática"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"desativar"</string>
<string name="sound_settings" msgid="8874581353127418308">"Som e vibração"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Configurações"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Legenda instantânea"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume diminuído para um nível mais seguro"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"O volume dos fones de ouvido está alto há mais tempo que o recomendado"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"O volume dos fones de ouvido excedeu o limite de segurança para esta semana"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controles de volume %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Chamadas e notificações farão o smartphone tocar (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"O áudio vai tocar em"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra de status"</string>
<string name="demo_mode" msgid="263484519766901593">"Modo de demonstração da interface do sistema"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 91a5f82fdab9..8cd2d77f9efb 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvat"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deconectează"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activează"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activează din nou automat mâine"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funcții precum Quick Share, Găsește-mi dispozitivul și locația dispozitivului folosesc Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Căști"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Dispozitivul este gestionat de unul dintre părinți. Părintele poate să vadă și să gestioneze informații cum ar fi aplicațiile pe care le folosești, locația ta și durata de folosire a dispozitivului."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Deblocat de TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protecție anti-furt\nDispozitiv blocat, prea multe încercări de deblocare"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Setări de sunet"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Adaugă subtitrări automate la fișierele media"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"dezactivează"</string>
<string name="sound_settings" msgid="8874581353127418308">"Sunete și vibrații"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Setări"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Subtitrări live"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volumul a fost redus la un nivel mai sigur"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Volumul căștilor a fost ridicat mai mult timp decât este recomandat"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Volumul căștilor a depășit limita de siguranță pentru săptămâna aceasta"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrații"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Comenzi de volum pentru %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Apelurile și notificările vor suna (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Se redă <xliff:g id="LABEL">%s</xliff:g> pe"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Conținutul audio se va reda pe"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Bară de stare"</string>
<string name="demo_mode" msgid="263484519766901593">"Mod demonstrativ pentru IU sistem"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index bc8e2f7fd097..614eb0617fb4 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сохранено"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"отключить"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активировать"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Включить завтра автоматически"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Bluetooth используется в сервисе \"Найти устройство\", таких функциях, как Быстрая отправка, и при определении местоположения устройства"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудиоустройство"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Этим устройством управляет один из твоих родителей. Он может видеть, например, какими приложениями ты пользуешься и где находишься, а также задавать определенные настройки (например, ограничивать время использования устройства)."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"Сеть VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Разблокировано агентом доверия"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Защита от кражи\nУстр-во заблокировано. Слишком много попыток."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>."</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Настройки звука"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматически добавлять субтитры"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"отключить"</string>
<string name="sound_settings" msgid="8874581353127418308">"Звук и вибрация"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Открыть настройки"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Автоматические субтитры"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Громкость уменьшена до безопасного уровня"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Вы используете наушники при высоком уровне громкости дольше, чем рекомендуется."</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Превышен безопасный лимит громкости наушников на этой неделе."</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"включить вибрацию"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s: регулировка громкости"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Для звонков и уведомлений включен звук (уровень громкости: <xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> – запущено здесь:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Проигрывание аудио:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Строка состояния"</string>
<string name="demo_mode" msgid="263484519766901593">"Интерфейс системы: деморежим"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 1e2c69546ee2..d1bcee422bd4 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"සුරැකිණි"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"විසන්ධි කරන්න"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"සක්‍රිය කරන්න"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"හෙට ස්වයංක්‍රීයව නැවත ක්‍රියාත්මක කරන්න"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ඉක්මන් බෙදා ගැනීම, මගේ උපාංගය සෙවීම, සහ උපාංග ස්ථානය වැනි විශේෂාංග බ්ලූටූත් භාවිත කරයි"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ශ්‍රව්‍ය"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"හෙඩ්සෙටය"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"මෙම උපාංගය ඔබගේ මාපියන් විසින් කළමනාකරණය කෙරේ. ඔබ භාවිත කරන යෙදුම්, ඔබගේ ස්ථානය සහ ඔබගේ තිර කාලය වැනි තොරතුරු ඔබගේ මාපියන්ට බැලීමට සහ කළමනාකරණය කිරීමට හැකිය."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent මඟින් අඟුලු දමා තබා ගන්න"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"සොරකම් ආරක්ෂණය\nඋපාංගය අගුළු දමා ඇත, අගුළු හැරීමේ උත්සාහයන් වැඩියි"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ශබ්ද සැකසීම්"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"මාධ්‍ය ස්වයංක්‍රීයව සිරස්තල"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"අබල කරන්න"</string>
<string name="sound_settings" msgid="8874581353127418308">"ශබ්ද සහ කම්පනය"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"සැකසීම්"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"සජීවී සිරස්තල"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"හඬ පරිමාව සුරක්ෂිත මට්ටමට අඩු කරන ලදි"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"නිර්දේශිත ප්‍රමාණයට වඩා වැඩි කාලයක් හෙඩ්ෆෝන් හඬ පරිමාව ඉහළ මට්ටමක පවතී"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"හෙඩ්ෆෝන් හඬ පරිමාව මෙම සතිය සඳහා සුරක්ෂිත සීමාව ඉක්මවා ඇත"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"කම්පනය"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"හඬ පරිමා පාලන %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"ඇමතුම් සහ දැනුම්දීම් (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>) නාද කරනු ඇත"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> වාදනය කරන්නේ"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ශ්‍රව්‍ය වාදනය වනු ඇත්තේ"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"පද්ධති UI සුසරකය"</string>
<string name="status_bar" msgid="4357390266055077437">"තත්ත්ව තීරුව"</string>
<string name="demo_mode" msgid="263484519766901593">"පද්ධති UI ආදර්ශන ප්‍රකාරය"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 1a3cac13bc5d..3e2b554f18b2 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uložené"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojiť"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovať"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automaticky zajtra znova zapnúť"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funkcie, ako sú Quick Share, Nájdi moje zariadenie a poloha zariadenia, používajú Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batéria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Náhlavná súprava"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Toto zariadenie spravuje tvoj rodič. Vidí a môže spravovať informácie, napríklad aplikácie, ktoré používaš, tvoju polohu a čas používania."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Odomknutie udržiava TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Ochrana pred krádež.\nZar. uzamknuté, priveľa pokusov o odomk."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Nastavenia zvuku"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatické titulkovanie médií"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"zakázať"</string>
<string name="sound_settings" msgid="8874581353127418308">"Zvuk a vibrácie"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Nastavenia"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Živý prepis"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Hlasitosť bola znížená na bezpečnejšiu úroveň"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Hlasitosť slúchadiel bola vysoká dlhšie, ako sa odporúča"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Hlasitosť slúchadiel prekročila bezpečný limit pre tento týždeň"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"zapnite vibrovanie"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Ovládacie prvky hlasitosti %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Hovory a upozornenia spustia zvonenie (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> sa prehráva v zar."</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk prehrá zariad.:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Tuner používateľského rozhrania systému"</string>
<string name="status_bar" msgid="4357390266055077437">"Stavový riadok"</string>
<string name="demo_mode" msgid="263484519766901593">"Ukážka používateľského rozhrania systému"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 0611b601725c..1ea97a1f4b43 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Shranjeno"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinitev povezave"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Samodejno znova vklopi jutri"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funkcije, kot sta Hitro deljenje in Poišči mojo napravo, ter lokacija naprave, uporabljajo Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvok"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalke z mikrofonom"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"To napravo upravlja tvoj starš. Lahko si ogleda in upravlja podatke, na primer katere aplikacije uporabljaš, tvojo lokacijo in koliko časa uporabljaš napravo."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ohranja odklenjeno"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Zaščita pred krajo\nNaprava je zaklenjena, preveč poskusov odklepanja"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Nastavitve zvoka"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sam. podnapisi predstavnosti"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Kontrolniki glasnosti za %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Klici in obvestila bodo pozvonili (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Predvajanje »<xliff:g id="LABEL">%s</xliff:g>« v"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvok bo predvajan v"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Uglaševalnik uporabniškega vmesnika sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Vrstica stanja"</string>
<string name="demo_mode" msgid="263484519766901593">"Predstavitveni način uporabniškega vmesnika sistema"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 447f162a653c..c3ccd81e6f62 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ruajtur"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"shkëput"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizo"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivizoje automatikisht nesër"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Veçoritë si \"Ndarja e shpejtë\", \"Gjej pajisjen time\" dhe vendndodhja e pajisjes përdorin Bluetooth-in"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kufje me mikrofon"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Kjo pajisje menaxhohet nga prindi yt. Prindi yt mund të shikojë dhe menaxhojë informacionet, si p.sh. aplikacionet që përdor, vendndodhjen tënde dhe kohën para ekranit."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Mbajtur shkyçur nga TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Mbrojtje nga vjedhja\nPajisja u kyç. Shumë përpjekje shkyçjeje"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Cilësimet e zërit"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Media me titra automatike"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"çaktivizo"</string>
<string name="sound_settings" msgid="8874581353127418308">"Tingulli dhe dridhjet"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Cilësimet"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Titrat në çast"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volumi është ulur në një nivel më të sigurt"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Volumi i kufjeve ka qenë i lartë për një kohë më të gjatë nga sa rekomandohet"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Volumi i kufjeve ka tejkaluar kufirin e sigurisë për këtë javë"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"lësho dridhje"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Kontrollet e volumit %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Do të bjerë zilja për telefonatat dhe njoftimet (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Po luhet <xliff:g id="LABEL">%s</xliff:g> në"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Do të luhet audio në"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizuesi i Sistemit të Ndërfaqes së Përdoruesit"</string>
<string name="status_bar" msgid="4357390266055077437">"Shiriti i statusit"</string>
<string name="demo_mode" msgid="263484519766901593">"Modaliteti i demonstrimit i ndërfaqes së përdoruesit të sistemit"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 5bc9db78f84d..2309d7de205c 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Овим уређајем управља родитељ. Родитељ може да види информације, као што су апликације које користиш, твоју локацију и време испред екрана, и да управља њима."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Поуздани агент спречава закључавање"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Заштита од крађе\nЗакљ. уређај, превише покушаја откључавања"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Подешавања звука"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Аутоматски титл за медије"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрација"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Контроле за јачину звука за %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Мелодија звона за позиве и обавештења је укључена (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> се пушта на"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Звук се пушта на"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Тјунер за кориснички интерфејс система"</string>
<string name="status_bar" msgid="4357390266055077437">"Статусна трака"</string>
<string name="demo_mode" msgid="263484519766901593">"Режим демонстрације за кориснички интерфејс система"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index bbc9fd63a235..be80f7aefd2d 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sparad"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koppla från"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivera"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivera automatiskt igen i morgon"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funktioner som Snabbdelning, Hitta min enhet och enhetens plats använder Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ljud"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Den här enheten hanteras av din förälder. Föräldern kan se och hantera information som vilka appar du använder, din plats och din skärmtid."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Hålls olåst med TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Stöldskydd\nEnheten har låsts på grund av för många försök"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ljudinställningar"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Texta media automatiskt"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"inaktivera"</string>
<string name="sound_settings" msgid="8874581353127418308">"Ljud och vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Inställningar"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Live Caption"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volymen har sänkts till en säkrare nivå"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Volymen i hörlurarna har varit hög längre än vad som rekommenderas"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Volymen i hörlurarna har överskridit den säkra gränsen för veckan"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Volymkontroller för %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Ringsignal används för samtal och aviseringar (volym: <xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Spelar upp <xliff:g id="LABEL">%s</xliff:g> på"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Ljud spelas upp på"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Inställningar för systemgränssnitt"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusfält"</string>
<string name="demo_mode" msgid="263484519766901593">"Demoläge för systemgränssnitt"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 4a735682491a..09998200c0f4 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Imehifadhiwa"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ondoa"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"anza kutumia"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Iwashe tena kesho kiotomatiki"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Vipengele kama vile Kutuma Haraka, Tafuta Kifaa Changu na mahali kifaa kilipo hutumia Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Sauti"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Vifaa vya sauti"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Kifaa hiki kinadhibitiwa na mzazi wako. Mzazi wako anaweza kuona na kudhibiti maelezo kama vile programu unazotumia, mahali ulipo na muda unaotumia kwenye kifaa."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Imefunguliwa na kipengele cha kutathmini hali ya kuaminika"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Kifaa cha ulinzi\ndhidi ya wizi kimefungwa, kuna majaribio mengi mno ya kukifungua"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Mipangilio ya sauti"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Wekea maudhui manukuu kiotomatiki"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tetema"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Vidhibiti %s vya sauti"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Itatoa mlio arifa ikitumwa na simu ikipigwa (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Inacheza <xliff:g id="LABEL">%s</xliff:g> kwenye"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Sauti itacheza kwenye"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Kirekebishi cha kiolesura cha mfumo"</string>
<string name="status_bar" msgid="4357390266055077437">"Sehemu ya kuonyesha hali"</string>
<string name="demo_mode" msgid="263484519766901593">"Hali ya onyesho la kirekebishi cha kiolesura cha mfumo"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 6a1e64eb49e4..486d7f97f16e 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"சேமிக்கப்பட்டது"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"இணைப்பு நீக்கும்"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"செயல்படுத்தும்"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"நாளைக்குத் தானாகவே மீண்டும் இயக்கப்படும்"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"விரைவுப் பகிர்தல், Find My Device போன்ற அம்சங்களும் சாதன இருப்பிடமும் புளூடூத்தைப் பயன்படுத்துகின்றன"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> பேட்டரி"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ஆடியோ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ஹெட்செட்"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"இந்தச் சாதனம் உங்கள் பெற்றோரால் நிர்வகிக்கப்படுகிறது. நீங்கள் பயன்படுத்தும் ஆப்ஸ், இருப்பிடம், பயன்படுத்திய நேரம் ஆகியவற்றைப் பார்க்கவும் நிர்வகிக்கவும் உங்கள் பெற்றோரால் முடியும்."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent இதைத் திறந்தே வைத்துள்ளது"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"திருட்டைத் தடுக்க\nசாதனம் பூட்டப்பட்டது, அதிகமான அன்லாக் முயற்சிகள்"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ஒலி அமைப்புகள்"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"வசன உரைகளைத் தானாக எழுதும்"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"முடக்கும்"</string>
<string name="sound_settings" msgid="8874581353127418308">"ஒலி &amp; அதிர்வு"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"அமைப்புகள்"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"உடனடி வசனம்"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"பாதுகாப்பான நிலைக்கு ஒலியளவு குறைக்கப்பட்டது"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"ஹெட்ஃபோன் ஒலியளவு பரிந்துரைக்கப்பட்டதைவிட அதிகளவில் நீண்ட நேரமாக உள்ளது"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"இந்த வாரம் ஹெட்ஃபோன் ஒலியளவு பாதுகாப்பு வரம்பைக் கடந்துவிட்டது"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"அதிர்வுறும்"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ஒலியளவுக் கட்டுப்பாடுகள்"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"அழைப்புகளும் அறிவிப்புகளும் வரும்போது ஒலிக்கச் செய்யும் (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"இதில் <xliff:g id="LABEL">%s</xliff:g> பிளே ஆகிறது"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"இல் ஆடியோ பிளே ஆகும்"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"நிலைப் பட்டி"</string>
<string name="demo_mode" msgid="263484519766901593">"சிஸ்டம் பயனர் இடைமுக டெமோ பயன்முறை"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 4a494ceca1b3..227ba5a2ea4a 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"సేవ్ చేయబడింది"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"డిస్‌కనెక్ట్ చేయండి"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"యాక్టివేట్ చేయండి"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"రేపు మళ్లీ ఆటోమేటిక్‌గా ఆన్ చేస్తుంది"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"క్విక్ షేర్, Find My Device, పరికర లొకేషన్ వంటి ఫీచర్‌లు బ్లూటూత్‌ను ఉపయోగిస్తాయి"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> బ్యాటరీ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ఆడియో"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"హెడ్‌సెట్"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ఈ పరికరాన్ని మీ తల్లి/తండ్రి మేనేజ్ చేస్తున్నారు. మీ తల్లి/తండ్రి, మీరు ఉపయోగించే యాప్‌లు, మీ లొకేషన్, అలాగే మీ పరికర వినియోగ వ్యవధి వంటి సమాచారాన్ని చూడగలరు, మేనేజ్ చేయగలరు."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ద్వారా అన్‌లాక్ చేయబడింది"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"దొంగతనం చేయడం నుండి రక్షణ\nపరికరం లాక్ చేయబడింది, చాలా ఎక్కువ అన్‌లాక్ ప్రయత్నాలు జరిగాయి"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ధ్వని సెట్టింగ్‌లు"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"మీడియాకు ఆటోమేటిక్ క్యాప్షన్‌లు"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"వైబ్రేట్"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s వాల్యూమ్ నియంత్రణలు"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"కాల్స్‌ మరియు నోటిఫికేషన్‌లు రింగ్ అవుతాయి (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>‌లో ప్లే అవుతోంది"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ఆడియో ప్లే అవుతుంది"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"సిస్టమ్ UI ట్యూనర్"</string>
<string name="status_bar" msgid="4357390266055077437">"స్టేటస్‌ బార్‌"</string>
<string name="demo_mode" msgid="263484519766901593">"సిస్టమ్ UI డెమో మోడ్"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index fd4bead3ec43..d1e186fa73b4 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"อุปกรณ์นี้จัดการโดยผู้ปกครอง ผู้ปกครองจะดูและจัดการข้อมูลต่างๆ ได้ เช่น แอปที่คุณใช้ ตำแหน่งของคุณ และเวลาอยู่หน้าจอ"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"ปลดล็อกไว้โดย TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"การป้องกันการโจรกรรม\nอุปกรณ์ถูกล็อก ลองปลดล็อกหลายครั้งเกินไป"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g> <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"การตั้งค่าเสียง"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"แสดงคำบรรยายสื่อโดยอัตโนมัติ"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"สั่น"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"ตัวควบคุมระดับเสียง %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"สายเรียกเข้าและการแจ้งเตือนจะส่งเสียง (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"กำลังเล่น <xliff:g id="LABEL">%s</xliff:g> ใน"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"เสียงจะเล่นต่อไป"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"ตัวรับสัญญาณ UI ระบบ"</string>
<string name="status_bar" msgid="4357390266055077437">"แถบสถานะ"</string>
<string name="demo_mode" msgid="263484519766901593">"โหมดสาธิต UI ของระบบ"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 5388d7a9ebd0..c929e4579df9 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -530,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Pinapamahalaan ng iyong magulang ang device na ito. Makikita at mapapamahalaan ng iyong magulang ang impormasyon tulad ng mga app na ginagamit mo, iyong lokasyon, at tagal ng paggamit mo sa device."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Pinanatiling naka-unlock ng TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Theft protection\nNa-lock ang device, sobrang pag-unlock"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Mga setting ng tunog"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"I-autocaption ang media"</string>
@@ -583,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"i-vibrate"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Mga kontrol ng volume ng %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Magri-ring kapag may mga tawag at notification (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Nagpe-play ang <xliff:g id="LABEL">%s</xliff:g> sa"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"I-play ang audio sa"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Tuner ng System UI"</string>
<string name="status_bar" msgid="4357390266055077437">"Status bar"</string>
<string name="demo_mode" msgid="263484519766901593">"Demo mode ng System UI"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index fbced87dea31..9407fbbbf3d9 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Kaydedildi"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"bağlantıyı kes"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"etkinleştir"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Yarın otomatik olarak tekrar aç"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Quick Share, Cihazımı Bul ve cihaz konumu gibi özellikler Bluetooth\'u kullanır"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pil düzeyi <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ses"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Mikrofonlu kulaklık"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Bu cihaz ebeveyniniz tarafından yönetiliyor. Kullandığınız uygulamalar, konumunuz ve ekran başında kalma süreniz gibi bilgiler ebeveyniniz tarafından görülüp yönetilebilir."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent tarafından kilit açık tutuldu"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Hırsızlık koruması\nCihaz kilitlendi, çok fazla kilit açma denemesi"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ses ayarları"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Otomatik medya altyazısı"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titreşim"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ses denetimleri"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Aramalar ve bildirimler telefonun zilini çaldıracak (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> şurada çalacak:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Ses şurada çalacak:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sistem Arayüzü Ayarlayıcısı"</string>
<string name="status_bar" msgid="4357390266055077437">"Durum çubuğu"</string>
<string name="demo_mode" msgid="263484519766901593">"Sistem kullanıcı arayüzü demo modu"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index c972090dc888..b7a4999865de 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Збережено"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"від’єднати"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активувати"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично ввімкнути знову завтра"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Такі функції, як швидкий обмін, \"Знайти пристрій\" і визначення місцезнаходження пристрою, використовують Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> заряду акумулятора"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудіопристрій"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Цим пристроєм керують твої батьки. Вони можуть бачити та контролювати, якими додатками ти користуєшся, де перебуваєш і скільки часу проводиш за пристроєм."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Розблоковує довірчий агент"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Захист від крадіжки\nПристрій заблоковано (забагато спроб)"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Налаштування звуку"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматичні субтитри (медіа)"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"вимкнути"</string>
<string name="sound_settings" msgid="8874581353127418308">"Звук і вібрація"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Налаштування"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Живі субтитри"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Гучність знижено до безпечнішого рівня"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Аудіо в навушниках відтворювалося з високою гучністю довше, ніж рекомендується"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Гучність навушників перевищила безпечний рівень, допустимий протягом тижня"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"увімкнути вібросигнал"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Регуляторів гучності: %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Для викликів і сповіщень налаштовано звуковий сигнал (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Відтворюється <xliff:g id="LABEL">%s</xliff:g> на:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудіо гратиме на:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Рядок стану"</string>
<string name="demo_mode" msgid="263484519766901593">"Демо-режим інтерфейсу системи"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index ede87fc2bf30..f9155a0e06e6 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ ہے"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"غیر منسلک کریں"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کریں"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"کل دوبارہ خودکار طور پر آن ہوگا"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"فوری اشتراک، میرا آلہ ڈھونڈیں، اور آلہ کے مقام جیسی خصوصیات بلوٹوتھ کا استعمال کرتی ہیں"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> بیٹری"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"آڈیو"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ہیڈ سیٹ"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"یہ آلہ آپ کے والدین کے زیر انتظام ہے۔ آپ کے والدین آپ کی استعمال والی ایپس، آپ کا مقام اور آپ کے اسکرین کے وقت جیسی معلومات کو دیکھ اور اس کا نظم کر سکتے ہیں۔"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"ٹرسٹ ایجنٹ نے غیر مقفل رکھا ہے"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"چوری سے تحفظ\n آلہ مقفل ہے، بہت زیادہ غیر مقفل کرنے کی کوششیں"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>۔ <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"صوتی ترتیبات"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"خودکار طور پر میڈیا پر کیپشن لگائیں"</string>
@@ -585,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"وائبریٹ"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"‏‎%s والیوم کے کنٹرولز"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"کالز اور اطلاعات موصول ہونے پر گھنٹی بجے گی (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> پر چل رہی ہے"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"آڈیو چلتی رہے گی"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"‏سسٹم UI ٹیونر"</string>
<string name="status_bar" msgid="4357390266055077437">"اسٹیٹس بار"</string>
<string name="demo_mode" msgid="263484519766901593">"‏سسٹم UI ڈیمو موڈ"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 6c36d3575d58..82b6b460ef67 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saqlangan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"uzish"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"faollashtirish"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ertaga yana avtomatik yoqilsin"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Tezkor ulashuv, Qurilmamni top va qurilma geolokatsiyasi kabi funksiyalar Bluetooth ishlatadi"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Garnitura"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Bu – ota-onangiz tomonidan boshqariladigan qurilma. Ota-onangiz siz foydalangan ilovalar, joylashuvingiz va qurilmadan foydalanish vaqti kabi axborotlarni koʻrishi va boshqarishi mumkin."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent tomonidan ochilgan"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Oʻgʻirlanishdan himoya\nQurilma qulflandi, juda koʻp urinildi"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Tovush sozlamalari"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Avtomatik taglavha yaratish"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"faolsizlantirish"</string>
<string name="sound_settings" msgid="8874581353127418308">"Tovush va tebranish"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Sozlamalar"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Jonli izoh"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Tovush xavfsiz darajaga pasaytirildi"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Quloqlik tavsiya etilganidan uzoqroq vaqt baland tovushda ishladi"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Quloqlik tovushi bu hafta xavfsiz balandlik limitidan oshib ketdi"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tebranish"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s tovush balandligi tugmalari"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Chaqiruvlar va bildirishnomalar jiringlaydi (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>da ijro etilmoqda"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio ijro etiladi"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"SystemUI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Holat qatori"</string>
<string name="demo_mode" msgid="263484519766901593">"Tizim interfeysi demo rejimi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 3a970fb3e625..2a8d4cdce031 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Đã lưu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ngắt kết nối"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"kích hoạt"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Tự động bật lại vào ngày mai"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Các tính năng như Chia sẻ nhanh, Tìm thiết bị của tôi và dịch vụ vị trí trên thiết bị có sử dụng Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> pin"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Âm thanh"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Tai nghe"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Thiết bị này do cha mẹ bạn quản lý. Cha mẹ có thể có thể xem và quản lý những thông tin như ứng dụng bạn dùng, vị trí của bạn và thời gian bạn sử dụng thiết bị."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Luôn được TrustAgent mở khóa"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Chống trộm\nĐã khoá thiết bị, quá nhiều lần mở khoá"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Cài đặt âm thanh"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Tự động tạo phụ đề cho nội dung nghe nhìn"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"tắt"</string>
<string name="sound_settings" msgid="8874581353127418308">"Âm thanh và chế độ rung"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Cài đặt"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Phụ đề trực tiếp"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Âm lượng đã giảm xuống mức an toàn hơn"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Bạn đã dùng tai nghe ở mức âm lượng cao lâu hơn khoảng thời gian khuyến nghị, điều này có thể gây tổn hại đến thính giác của bạn"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Âm lượng tai nghe đã vượt quá giới hạn an toàn của tuần này"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rung"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Điều khiển âm lượng %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Cuộc gọi và thông báo sẽ đổ chuông (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Đang phát <xliff:g id="LABEL">%s</xliff:g> trên"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Âm thanh sẽ phát ra"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Bộ điều hướng giao diện người dùng hệ thống"</string>
<string name="status_bar" msgid="4357390266055077437">"Thanh trạng thái"</string>
<string name="demo_mode" msgid="263484519766901593">"Chế độ thử nghiệm giao diện người dùng hệ thống"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index fbdc86c90868..ddef35f6d1b8 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已保存"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"断开连接"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"启用"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自动重新开启"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"快速分享、查找我的设备、设备位置信息等功能会使用蓝牙"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> 的电量"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音频"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳机"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"此设备由您的家长管理。您的家长可以查看和管理相关信息,例如您使用的应用、您的位置信息和设备使用时间。"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"由 TrustAgent 保持解锁状态"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"防盗保护\n设备已锁定,因为尝试解锁的次数过多"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>(<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>)"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"声音设置"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"自动生成媒体字幕"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"停用"</string>
<string name="sound_settings" msgid="8874581353127418308">"声音和振动"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"设置"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"实时字幕"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"音量已降至较安全的水平"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"您以高音量使用耳机的时长超过了建议值"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"耳机音量已超出这周的安全上限"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"振动"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s音量控件"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"有来电和通知时会响铃 (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>播放位置:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"音频播放位置:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"系统界面调节工具"</string>
<string name="status_bar" msgid="4357390266055077437">"状态栏"</string>
<string name="demo_mode" msgid="263484519766901593">"系统界面演示模式"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 4579ca2436cb..dbf2cc0a25c8 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"解除連結"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟動"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"「快速共享」、「尋找我的裝置」和裝置位置等功能都會使用藍牙"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"此裝置由你的家長管理。家長可以查看及管理裝置上的資料,例如你使用的應用程式、位置和裝置使用時間。"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"由信任的代理保持解鎖狀態"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"防盜保護\n嘗試解鎖次數過多,裝置已上鎖"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>。<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"音效設定"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"自動為媒體加入字幕"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"停用"</string>
<string name="sound_settings" msgid="8874581353127418308">"音效和震動"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"設定"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"即時字幕"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"音量已降至較安全的水平"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"以高音量使用耳機的時間已超過建議範圍"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"耳機音量已超過本週安全限制"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s音量控制項"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"有來電和通知時會發出鈴聲 (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"正在播放「<xliff:g id="LABEL">%s</xliff:g>」的裝置:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"音訊播放媒體"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"系統使用者介面調諧器"</string>
<string name="status_bar" msgid="4357390266055077437">"狀態列"</string>
<string name="demo_mode" msgid="263484519766901593">"系統使用者介面示範模式"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 05d9ec319f09..d92de1cf6af9 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"取消連結"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟用"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"快速分享、尋找我的裝置和裝置位置資訊等功能都會使用藍牙"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -529,9 +527,10 @@
<string name="monitoring_description_personal_profile_named_vpn" msgid="5083909710727365452">"你的個人應用程式已透過「<xliff:g id="VPN_APP">%1$s</xliff:g>」連線到網際網路。請注意,VPN 供應商可以看見你的網路活動,包括電子郵件和瀏覽資料。"</string>
<string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string>
<string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"開啟 VPN 設定"</string>
- <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"這個裝置是由你的家長管理。家長可以查看及管理裝置上的資訊,例如你使用的應用程式、所在位置和裝置使用時間。"</string>
+ <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"這個裝置是由你的家長管理。家長可以查看及管理裝置上的資訊,例如你使用的應用程式、所在位置和螢幕時間。"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"由 TrustAgent 維持解鎖狀態"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"防盜保護\n解鎖失敗次數過多,裝置已鎖定"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>。<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"音效設定"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"自動產生媒體字幕"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"停用"</string>
<string name="sound_settings" msgid="8874581353127418308">"音效與震動"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"設定"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"即時字幕"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"音量已調低至安全範圍"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"耳機以高音量播放已超過建議時間"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"耳罩式耳機的音量已超過本週的安全限制"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"「%s」音量控制項"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"有來電和通知時會響鈴 (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"正在播放「<xliff:g id="LABEL">%s</xliff:g>」的裝置:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"將播放音訊的媒體:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"系統使用者介面調整精靈"</string>
<string name="status_bar" msgid="4357390266055077437">"狀態列"</string>
<string name="demo_mode" msgid="263484519766901593">"系統 UI 展示模式"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 46ec239ed950..9cd70829c30c 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -268,10 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ilondoloziwe"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"nqamula"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"yenza kusebenze"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (414836329962473906) -->
- <skip />
- <!-- no translation found for turn_on_bluetooth_auto_info (8831410009251539988) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Vula ngokuzenzekela futhi kusasa"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Izakhi ezifana Nokwabelana Ngokushesha, okuthi Thola Idivayisi Yami, kanye nendawo yedivayisi zisebenzisa i-Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ibhethri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Umsindo"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ihedisethi"</string>
@@ -532,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Le divayisi iphethwe ngumzali wakho. Umzali wakho angabona futhi aphathe ulwazi olunjengezinhlelo zokusebenza ozisebenzisayo, indawo yakho, kanye nesikhathi sesikrini."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"I-VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Igcinwa ivuliwe ngo-TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Isivikelo sokweba\nIdivayisi ikhiyiwe, imizamo yokuvula eminingi kakhulu"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Izilungiselelo zomsindo"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Yenza amagama-ngcazo ngokuzenzakalela emidiya"</string>
@@ -541,8 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"khubaza"</string>
<string name="sound_settings" msgid="8874581353127418308">"Umsindo nokudlidliza"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Amasethingi"</string>
- <!-- no translation found for volume_panel_captioning_title (5984936949147684357) -->
- <skip />
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Okushuthwe Bukhoma"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Ivolumu yehliselwe kuleveli ephephile"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Ivolumu yama-headphone beyiphezulu isikhathi eside kunokunconyiwe"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Ivolumu yama-headphone ibe phezulu kunokunconyiwe, okungalimaza ukuzwa kwakho"</string>
@@ -586,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dlidliza"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s izilawuli zevolomu"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Amakholi nezaziso zizokhala (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Idlala ku-<xliff:g id="LABEL">%s</xliff:g>"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Umsindo uzodlala"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Isishuni se-UI yesistimu"</string>
<string name="status_bar" msgid="4357390266055077437">"Ibha yesimo"</string>
<string name="demo_mode" msgid="263484519766901593">"Imodi yedemo ye-UI yesistimu"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 65120a99a6b5..5436642dc5a1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -608,8 +608,6 @@
<dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
<dimen name="volume_panel_corner_radius">52dp</dimen>
- <dimen name="volume_panel_bottom_bar_horizontal_padding">24dp</dimen>
- <dimen name="volume_panel_bottom_bar_bottom_padding">20dp</dimen>
<!-- Size of each item in the ringer selector drawer. -->
<dimen name="volume_ringer_drawer_item_size">42dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a7d93e70fda3..53ad344189d8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1522,6 +1522,9 @@
<string name="volume_stream_content_description_vibrate_a11y">%1$s. Tap to set to vibrate.</string>
<string name="volume_stream_content_description_mute_a11y">%1$s. Tap to mute.</string>
+ <!-- Label for button to enabled/disable live caption [CHAR_LIMIT=30] -->
+ <string name="volume_panel_noise_control_title">Noise Control</string>
+
<string name="volume_ringer_change">Tap to change ringer mode</string>
<!-- Hint for accessibility. For example: double tap to mute [CHAR_LIMIT=NONE] -->
@@ -3126,6 +3129,8 @@
<!-- [CHAR LIMIT=25] Long label used by Note Task Shortcut -->
<string name="note_task_shortcut_long_label">Note-taking, <xliff:g id="note_taking_app" example="Note-taking App">%1$s</xliff:g></string>
+ <!-- [CHAR LIMIT=NONE] Output switch chip text during broadcasting -->
+ <string name="audio_sharing_description">Sharing audio</string>
<!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, media app is broadcasting -->
<string name="broadcasting_description_is_broadcasting">Broadcasting</string>
<!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, title -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f1d4d71d1cc4..617eadbd447c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -953,8 +953,11 @@
<item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
</style>
- <style name="Theme.VolumePanelActivity"
- parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
+ <style name="Theme.VolumePanelActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowNoTitle">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">true</item>
@@ -962,6 +965,12 @@
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
</style>
+ <style name="Theme.VolumePanelActivity.Popup" parent="@style/Theme.SystemUI.Dialog">
+ <item name="android:dialogCornerRadius">44dp</item>
+ <item name="android:colorBackground">?androidprv:attr/materialColorSurfaceContainerHigh
+ </item>
+ </style>
+
<style name="Theme.UserSwitcherFullscreenDialog" parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
<item name="android:statusBarColor">@color/user_switcher_fullscreen_bg</item>
<item name="android:windowBackground">@color/user_switcher_fullscreen_bg</item>
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
index 2d854d934ca2..02ee2c951453 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
@@ -16,18 +16,8 @@
package com.android.keyguard.logging
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel.DEBUG
-import com.android.systemui.log.dagger.BiometricLog
-import javax.inject.Inject
-
-/** Helper class for logging for [com.android.systemui.biometrics.FaceHelpMessageDeferral] */
-@SysUISingleton
-class FaceMessageDeferralLogger
-@Inject
-constructor(@BiometricLog private val logBuffer: LogBuffer) :
- BiometricMessageDeferralLogger(logBuffer, "FaceMessageDeferralLogger")
open class BiometricMessageDeferralLogger(
private val logBuffer: LogBuffer,
diff --git a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
index e8499d3ca9a9..ca83724aaf07 100644
--- a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
@@ -143,7 +143,7 @@ class CameraAvailabilityListener(
private fun notifyCameraActive(info: CameraProtectionInfo) {
listeners.forEach {
- it.onApplyCameraProtection(info.cutoutProtectionPath, info.cutoutBounds)
+ it.onApplyCameraProtection(info.cutoutProtectionPath, info.bounds)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt b/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt
index 6314bd9a5615..9357056a0850 100644
--- a/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt
@@ -23,6 +23,6 @@ data class CameraProtectionInfo(
val logicalCameraId: String,
val physicalCameraId: String?,
val cutoutProtectionPath: Path,
- val cutoutBounds: Rect,
+ val bounds: Rect,
val displayUniqueId: String?,
)
diff --git a/packages/SystemUI/src/com/android/systemui/SysUICutoutProvider.kt b/packages/SystemUI/src/com/android/systemui/SysUICutoutProvider.kt
index aad934124dfb..7309599d9c04 100644
--- a/packages/SystemUI/src/com/android/systemui/SysUICutoutProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/SysUICutoutProvider.kt
@@ -17,8 +17,12 @@
package com.android.systemui
import android.content.Context
+import android.graphics.Rect
+import android.util.RotationUtils
+import android.view.Display
import android.view.DisplayCutout
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.display.naturalBounds
import javax.inject.Inject
@SysUISingleton
@@ -33,15 +37,43 @@ constructor(
cameraProtectionLoader.loadCameraProtectionInfoList()
}
- fun cutoutInfoForCurrentDisplay(): SysUICutoutInformation? {
+ /**
+ * Returns the [SysUICutoutInformation] for the current display and the current rotation.
+ *
+ * This means that the bounds of the display cutout and the camera protection will be
+ * adjusted/rotated for the current rotation.
+ */
+ fun cutoutInfoForCurrentDisplayAndRotation(): SysUICutoutInformation? {
val display = context.display
val displayCutout: DisplayCutout = display.cutout ?: return null
+ return SysUICutoutInformation(displayCutout, getCameraProtectionForDisplay(display))
+ }
+
+ private fun getCameraProtectionForDisplay(display: Display): CameraProtectionInfo? {
val displayUniqueId: String? = display.uniqueId
if (displayUniqueId.isNullOrEmpty()) {
- return SysUICutoutInformation(displayCutout, cameraProtection = null)
+ return null
}
- val cameraProtection: CameraProtectionInfo? =
+ val cameraProtection: CameraProtectionInfo =
cameraProtectionList.firstOrNull { it.displayUniqueId == displayUniqueId }
- return SysUICutoutInformation(displayCutout, cameraProtection)
+ ?: return null
+ val adjustedBoundsForRotation =
+ calculateCameraProtectionBoundsForRotation(display, cameraProtection.bounds)
+ return cameraProtection.copy(bounds = adjustedBoundsForRotation)
+ }
+
+ private fun calculateCameraProtectionBoundsForRotation(
+ display: Display,
+ originalProtectionBounds: Rect,
+ ): Rect {
+ val displayNaturalBounds = display.naturalBounds
+ val rotatedBoundsOut = Rect(originalProtectionBounds)
+ RotationUtils.rotateBounds(
+ /* inOutBounds = */ rotatedBoundsOut,
+ /* parentWidth = */ displayNaturalBounds.width(),
+ /* parentHeight = */ displayNaturalBounds.height(),
+ /* rotation = */ display.rotation
+ )
+ return rotatedBoundsOut
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 9fd060255e9b..e0f73a63113a 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -229,6 +229,9 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
private void fetchCurrentActiveOps() {
List<AppOpsManager.PackageOps> packageOps = mAppOps.getPackagesForOps(OPS);
+ if (packageOps == null) {
+ return;
+ }
for (AppOpsManager.PackageOps op : packageOps) {
for (AppOpsManager.OpEntry entry : op.getOps()) {
for (Map.Entry<String, AppOpsManager.AttributedOpEntry> attributedOpEntry :
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
index 8c68eac84963..90d06fb0bec1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
@@ -18,14 +18,16 @@ package com.android.systemui.biometrics
import android.content.res.Resources
import com.android.keyguard.logging.BiometricMessageDeferralLogger
-import com.android.keyguard.logging.FaceMessageDeferralLogger
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.dagger.BiometricLog
import com.android.systemui.res.R
import java.io.PrintWriter
import java.util.Objects
+import java.util.UUID
import javax.inject.Inject
@SysUISingleton
@@ -33,14 +35,16 @@ class FaceHelpMessageDeferralFactory
@Inject
constructor(
@Main private val resources: Resources,
- private val logBuffer: FaceMessageDeferralLogger,
+ @BiometricLog private val logBuffer: LogBuffer,
private val dumpManager: DumpManager
) {
fun create(): FaceHelpMessageDeferral {
+ val id = UUID.randomUUID().toString()
return FaceHelpMessageDeferral(
resources = resources,
- logBuffer = logBuffer,
+ logBuffer = BiometricMessageDeferralLogger(logBuffer, "FaceHelpMessageDeferral[$id]"),
dumpManager = dumpManager,
+ id = id,
)
}
}
@@ -51,15 +55,17 @@ constructor(
*/
class FaceHelpMessageDeferral(
resources: Resources,
- logBuffer: FaceMessageDeferralLogger,
- dumpManager: DumpManager
+ logBuffer: BiometricMessageDeferralLogger,
+ dumpManager: DumpManager,
+ val id: String,
) :
BiometricMessageDeferral(
resources.getIntArray(R.array.config_face_help_msgs_defer_until_timeout).toHashSet(),
resources.getIntArray(R.array.config_face_help_msgs_ignore).toHashSet(),
resources.getFloat(R.dimen.config_face_help_msgs_defer_until_timeout_threshold),
logBuffer,
- dumpManager
+ dumpManager,
+ id,
)
/**
@@ -72,7 +78,8 @@ open class BiometricMessageDeferral(
private val acquiredInfoToIgnore: Set<Int>,
private val threshold: Float,
private val logBuffer: BiometricMessageDeferralLogger,
- dumpManager: DumpManager
+ dumpManager: DumpManager,
+ id: String,
) : Dumpable {
private val acquiredInfoToFrequency: MutableMap<Int, Int> = HashMap()
private val acquiredInfoToHelpString: MutableMap<Int, String> = HashMap()
@@ -80,7 +87,10 @@ open class BiometricMessageDeferral(
private var totalFrames = 0
init {
- dumpManager.registerDumpable(this.javaClass.name, this)
+ dumpManager.registerNormalDumpable(
+ "${this.javaClass.name}[$id]",
+ this,
+ )
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 25ccc1658744..357eca37ba37 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -93,11 +93,13 @@ public class BrightLineFalsingManager implements FalsingManager {
@Override
public void onSessionEnded() {
mLastProximityEvent = null;
+ mHistoryTracker.removeBeliefListener(mBeliefListener);
mClassifiers.forEach(FalsingClassifier::onSessionEnded);
}
@Override
public void onSessionStarted() {
+ mHistoryTracker.addBeliefListener(mBeliefListener);
mClassifiers.forEach(FalsingClassifier::onSessionStarted);
}
};
@@ -200,7 +202,6 @@ public class BrightLineFalsingManager implements FalsingManager {
mDataProvider.addSessionListener(mSessionListener);
mDataProvider.addGestureCompleteListener(mGestureFinalizedListener);
- mHistoryTracker.addBeliefListener(mBeliefListener);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 5b1082acd59f..3819e614aca0 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -27,6 +27,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
@@ -39,6 +40,7 @@ import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChang
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.kotlin.BooleanFlowOperators;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
@@ -67,6 +69,7 @@ class FalsingCollectorImpl implements FalsingCollector {
private final StatusBarStateController mStatusBarStateController;
private final KeyguardStateController mKeyguardStateController;
private final Lazy<ShadeInteractor> mShadeInteractorLazy;
+ private final Lazy<CommunalInteractor> mCommunalInteractorLazy;
private final BatteryController mBatteryController;
private final DockManager mDockManager;
private final DelayableExecutor mMainExecutor;
@@ -76,6 +79,7 @@ class FalsingCollectorImpl implements FalsingCollector {
private int mState;
private boolean mShowingAod;
+ private boolean mShowingCommunalHub;
private boolean mScreenOn;
private boolean mSessionStarted;
private MotionEvent mPendingDownEvent;
@@ -145,7 +149,8 @@ class FalsingCollectorImpl implements FalsingCollector {
@Main DelayableExecutor mainExecutor,
JavaAdapter javaAdapter,
SystemClock systemClock,
- Lazy<SelectedUserInteractor> userInteractor) {
+ Lazy<SelectedUserInteractor> userInteractor,
+ Lazy<CommunalInteractor> communalInteractorLazy) {
mFalsingDataProvider = falsingDataProvider;
mFalsingManager = falsingManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -160,6 +165,7 @@ class FalsingCollectorImpl implements FalsingCollector {
mJavaAdapter = javaAdapter;
mSystemClock = systemClock;
mUserInteractor = userInteractor;
+ mCommunalInteractorLazy = communalInteractorLazy;
}
@Override
@@ -176,6 +182,13 @@ class FalsingCollectorImpl implements FalsingCollector {
mShadeInteractorLazy.get().isQsExpanded(),
this::onQsExpansionChanged
);
+ final CommunalInteractor communalInteractor = mCommunalInteractorLazy.get();
+ mJavaAdapter.alwaysCollectFlow(
+ BooleanFlowOperators.INSTANCE.and(
+ communalInteractor.isCommunalEnabled(),
+ communalInteractor.isCommunalShowing()),
+ this::onShowingCommunalHubChanged
+ );
mBatteryController.addCallback(mBatteryListener);
mDockManager.addListener(mDockEventListener);
@@ -205,6 +218,12 @@ class FalsingCollectorImpl implements FalsingCollector {
}
}
+ private void onShowingCommunalHubChanged(boolean isShowing) {
+ logDebug("REAL: onShowingCommunalHubChanged(" + isShowing + ")");
+ mShowingCommunalHub = isShowing;
+ updateSessionActive();
+ }
+
@Override
public boolean shouldEnforceBouncer() {
return false;
@@ -333,7 +352,10 @@ class FalsingCollectorImpl implements FalsingCollector {
}
private boolean shouldSessionBeActive() {
- return mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod;
+ return mScreenOn
+ && (mState == StatusBarState.KEYGUARD)
+ && !mShowingAod
+ && !mShowingCommunalHub;
}
private void updateSessionActive() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
index 2e861c399ee9..57e9ade30bb5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
@@ -17,23 +17,27 @@
package com.android.systemui.classifier.domain.interactor
import android.view.MotionEvent
+import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.FalsingClassifier
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorActual
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.FalsingManager
import javax.inject.Inject
/**
- * Exposes the subset of the [FalsingCollector] API that's required by external callers.
+ * Exposes the subset of the [FalsingCollector] and [FalsingManager] APIs that's required by
+ * external callers.
*
- * E.g. methods of [FalsingCollector] that are not exposed by this class don't need to be invoked by
- * external callers as they're already called by the scene framework.
+ * E.g. methods of the above APIs that are not exposed by this class either don't need to be invoked
+ * by external callers (as they're already called by the scene framework) or haven't been added yet.
*/
@SysUISingleton
class FalsingInteractor
@Inject
constructor(
@FalsingCollectorActual private val collector: FalsingCollector,
+ private val manager: FalsingManager,
) {
/**
* Notifies of a [MotionEvent] that passed through the UI.
@@ -62,4 +66,9 @@ constructor(
fun updateFalseConfidence(
result: FalsingClassifier.Result,
) = collector.updateFalseConfidence(result)
+
+ /** Returns `true` if the gesture should be rejected. */
+ fun isFalseTouch(
+ @Classifier.InteractionType interactionType: Int,
+ ): Boolean = manager.isFalseTouch(interactionType)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
index 201be51b873c..e2fed6d0ea20 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
@@ -21,8 +21,8 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.dagger.CommunalTableLog
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index 9e68ff88622c..f4a3bcb7a0fa 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -22,7 +22,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.shared.flag.SceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -33,14 +32,10 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/** Encapsulates the state of communal mode. */
interface CommunalRepository {
- /** Whether the communal hub is showing. */
- val isCommunalHubShowing: Flow<Boolean>
-
/**
* Target scene as requested by the underlying [SceneTransitionLayout] or through
* [setDesiredScene].
@@ -99,11 +94,4 @@ constructor(
override fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
_transitionState.value = transitionState
}
-
- override val isCommunalHubShowing: Flow<Boolean> =
- if (sceneContainerFlags.isEnabled()) {
- sceneContainerRepository.currentScene.map { sceneKey -> sceneKey == SceneKey.Communal }
- } else {
- desiredScene.map { sceneKey -> sceneKey == CommunalSceneKey.Communal }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 636ea42ac88e..d0044a4c029e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -42,6 +42,9 @@ import com.android.systemui.log.dagger.CommunalLog
import com.android.systemui.log.dagger.CommunalTableLog
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.smartspace.data.repository.SmartspaceRepository
import com.android.systemui.util.kotlin.BooleanFlowOperators.and
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
@@ -57,6 +60,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@@ -75,9 +79,11 @@ constructor(
mediaRepository: CommunalMediaRepository,
smartspaceRepository: SmartspaceRepository,
keyguardInteractor: KeyguardInteractor,
- private val communalSettingsInteractor: CommunalSettingsInteractor,
+ communalSettingsInteractor: CommunalSettingsInteractor,
private val appWidgetHost: CommunalAppWidgetHost,
private val editWidgetsActivityStarter: EditWidgetsActivityStarter,
+ sceneInteractor: SceneInteractor,
+ sceneContainerFlags: SceneContainerFlags,
@CommunalLog logBuffer: LogBuffer,
@CommunalTableLog tableLogBuffer: TableLogBuffer,
) {
@@ -89,8 +95,7 @@ constructor(
val editModeOpen: StateFlow<Boolean> = _editModeOpen.asStateFlow()
/** Whether communal features are enabled. */
- val isCommunalEnabled: Boolean
- get() = communalSettingsInteractor.isCommunalEnabled.value
+ val isCommunalEnabled: StateFlow<Boolean> = communalSettingsInteractor.isCommunalEnabled
/** Whether communal features are enabled and available. */
val isCommunalAvailable: Flow<Boolean> =
@@ -173,8 +178,14 @@ constructor(
*/
// TODO(b/323215860): rename to something more appropriate after cleaning up usages
val isCommunalShowing: Flow<Boolean> =
- communalRepository.desiredScene
- .map { it == CommunalSceneKey.Communal }
+ flow { emit(sceneContainerFlags.isEnabled()) }
+ .flatMapLatest { sceneContainerEnabled ->
+ if (sceneContainerEnabled) {
+ sceneInteractor.currentScene.map { it == SceneKey.Communal }
+ } else {
+ desiredScene.map { it == CommunalSceneKey.Communal }
+ }
+ }
.distinctUntilChanged()
.onEach { showing ->
logger.i({ "Communal is ${if (bool1) "showing" else "gone"}" }) { bool1 = showing }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
index 25dfc02a5d98..2b7db148582d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
@@ -17,7 +17,6 @@
package com.android.systemui.communal.domain.interactor
import android.provider.Settings
-import com.android.systemui.communal.data.repository.CommunalRepository
import com.android.systemui.communal.data.repository.CommunalTutorialRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -51,7 +50,6 @@ constructor(
@Application private val scope: CoroutineScope,
private val communalTutorialRepository: CommunalTutorialRepository,
keyguardInteractor: KeyguardInteractor,
- private val communalRepository: CommunalRepository,
private val communalSettingsInteractor: CommunalSettingsInteractor,
communalInteractor: CommunalInteractor,
@CommunalTableLog tableLogBuffer: TableLogBuffer,
@@ -92,7 +90,7 @@ constructor(
if (tutorialSettingState == Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) {
return@flatMapLatest flowOf(null)
}
- communalRepository.isCommunalHubShowing.map { isCommunalShowing ->
+ communalInteractor.isCommunalShowing.map { isCommunalShowing ->
nextStateAfterTransition(
tutorialSettingState,
isCommunalShowing,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt
index 2d9dd50b6d11..1a323a75f8e7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt
@@ -47,7 +47,7 @@ constructor(
private var communalTutorialIndicatorHandle: DisposableHandle? = null
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!communalInteractor.isCommunalEnabled) {
+ if (!communalInteractor.isCommunalEnabled.value) {
return
}
val padding =
@@ -79,7 +79,7 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!communalInteractor.isCommunalEnabled) {
+ if (!communalInteractor.isCommunalEnabled.value) {
return
}
communalTutorialIndicatorHandle =
@@ -90,7 +90,7 @@ constructor(
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!communalInteractor.isCommunalEnabled) {
+ if (!communalInteractor.isCommunalEnabled.value) {
return
}
val tutorialIndicatorId = R.id.communal_tutorial_indicator
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index dee7a0c4f717..8a7b5ebedb7a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -23,7 +23,7 @@ import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.communal.widgets.WidgetConfigurator
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHost
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index efbb11e824c9..bfe751af7154 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -25,7 +25,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.dagger.MediaModule
import javax.inject.Inject
import javax.inject.Named
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index febfd4c95db1..fc9a7df4744d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -24,9 +24,9 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.media.controls.ui.MediaHostState
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.dagger.MediaModule
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamMediaEntryComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamMediaEntryComplication.java
index 6a7278529a54..bdefc4d2cbba 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamMediaEntryComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamMediaEntryComplication.java
@@ -28,7 +28,7 @@ import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.complication.dagger.DreamMediaEntryComplicationComponent;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.media.controls.ui.MediaCarouselController;
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController;
import com.android.systemui.media.dream.MediaDreamComplication;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/NotifInflation.kt b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/NotifInflation.kt
new file mode 100644
index 000000000000..231fb2dd16c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/NotifInflation.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dagger.qualifiers
+
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class NotifInflation
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
index 55d2bfcc8911..6bfe8d91b5fc 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
@@ -29,6 +29,7 @@ import com.android.systemui.deviceentry.shared.model.FingerprintMessage
import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -41,9 +42,8 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
/**
- * BiometricMessage business logic. Filters biometric error/acquired/fail/success events for
- * authentication events that should never surface a message to the user at the current device
- * state.
+ * BiometricMessage business logic. Filters biometric error/fail/success events for authentication
+ * events that should never surface a message to the user at the current device state.
*/
@ExperimentalCoroutinesApi
@SysUISingleton
@@ -54,7 +54,8 @@ constructor(
fingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
fingerprintPropertyInteractor: FingerprintPropertyInteractor,
faceAuthInteractor: DeviceEntryFaceAuthInteractor,
- biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
+ private val biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
+ faceHelpMessageDeferralInteractor: FaceHelpMessageDeferralInteractor,
) {
private val faceHelp: Flow<HelpFaceAuthenticationStatus> =
faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>()
@@ -130,19 +131,24 @@ constructor(
)
private val faceHelpMessage: Flow<FaceMessage> =
- biometricSettingsInteractor.fingerprintAndFaceEnrolledAndEnabled
- .flatMapLatest { fingerprintAndFaceEnrolledAndEnabled ->
+ faceHelp
+ .filterNot {
+ // Message deferred to potentially show at face timeout error instead
+ faceHelpMessageDeferralInteractor.shouldDefer(it.msgId)
+ }
+ .sample(biometricSettingsInteractor.fingerprintAndFaceEnrolledAndEnabled, ::Pair)
+ .filter { (faceAuthHelpStatus, fingerprintAndFaceEnrolledAndEnabled) ->
if (fingerprintAndFaceEnrolledAndEnabled) {
- faceHelp.filter { faceAuthHelpStatus ->
- coExFaceAcquisitionMsgIdsToShow.contains(faceAuthHelpStatus.msgId)
- }
+ // Show only some face help messages if fingerprint is also enrolled
+ coExFaceAcquisitionMsgIdsToShow.contains(faceAuthHelpStatus.msgId)
} else {
- faceHelp
+ // Show all face help messages if only face is enrolled
+ true
}
}
- .sample(biometricSettingsInteractor.faceAuthCurrentlyAllowed, ::Pair)
- .filter { (_, faceAuthCurrentlyAllowed) -> faceAuthCurrentlyAllowed }
- .map { (status, _) -> FaceMessage(status.msg) }
+ .sample(biometricSettingsInteractor.faceAuthCurrentlyAllowed, ::toTriple)
+ .filter { (_, _, faceAuthCurrentlyAllowed) -> faceAuthCurrentlyAllowed }
+ .map { (status, _, _) -> FaceMessage(status.msg) }
private val faceFailureMessage: Flow<FaceMessage> =
faceFailure
@@ -159,12 +165,18 @@ constructor(
}
.map { (status, _) ->
when {
- status.isTimeoutError() -> FaceTimeoutMessage(status.msg)
+ status.isTimeoutError() -> {
+ val deferredMessage = faceHelpMessageDeferralInteractor.getDeferredMessage()
+ if (deferredMessage != null) {
+ FaceMessage(deferredMessage.toString())
+ } else {
+ FaceTimeoutMessage(status.msg)
+ }
+ }
else -> FaceMessage(status.msg)
}
}
- // TODO(b/317215391): support showing face acquired messages on timeout + face lockout errors
val faceMessage: Flow<FaceMessage> =
merge(
faceHelpMessage,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt
index 4515fcb545b3..96171aa6566e 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt
@@ -33,6 +33,7 @@ constructor(
) {
val fingerprintAuthCurrentlyAllowed: Flow<Boolean> =
repository.isFingerprintAuthCurrentlyAllowed
+ val faceAuthEnrolledAndEnabled: Flow<Boolean> = repository.isFaceAuthEnrolledAndEnabled
val faceAuthCurrentlyAllowed: Flow<Boolean> = repository.isFaceAuthCurrentlyAllowed
/** Whether both fingerprint and face are enrolled and enabled for device entry. */
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
new file mode 100644
index 000000000000..fd6fbc9610f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import android.hardware.face.FaceManager
+import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
+import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.launch
+
+/**
+ * FaceHelpMessageDeferral business logic. Processes face acquired and face help authentication
+ * events to determine whether a face auth event should be displayed to the user immediately or when
+ * a [FaceManager.FACE_ERROR_TIMEOUT] is received.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class FaceHelpMessageDeferralInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ faceAuthInteractor: DeviceEntryFaceAuthInteractor,
+ private val biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
+ faceHelpMessageDeferralFactory: FaceHelpMessageDeferralFactory,
+) {
+ private val faceHelpMessageDeferral = faceHelpMessageDeferralFactory.create()
+ private val faceAcquired: Flow<AcquiredFaceAuthenticationStatus> =
+ faceAuthInteractor.authenticationStatus.filterIsInstance<AcquiredFaceAuthenticationStatus>()
+ private val faceHelp: Flow<HelpFaceAuthenticationStatus> =
+ faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>()
+
+ init {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ startUpdatingFaceHelpMessageDeferral()
+ }
+ }
+
+ /**
+ * If the given [HelpFaceAuthenticationStatus] msgId should be deferred to
+ * [FaceManager.FACE_ERROR_TIMEOUT].
+ */
+ fun shouldDefer(msgId: Int): Boolean {
+ return faceHelpMessageDeferral.shouldDefer(msgId)
+ }
+
+ /**
+ * Message that was deferred to show at [FaceManager.FACE_ERROR_TIMEOUT], if any. Returns null
+ * if there are currently no valid deferred messages.
+ */
+ fun getDeferredMessage(): CharSequence? {
+ return faceHelpMessageDeferral.getDeferredMessage()
+ }
+
+ private fun startUpdatingFaceHelpMessageDeferral() {
+ scope.launch {
+ biometricSettingsInteractor.faceAuthEnrolledAndEnabled
+ .flatMapLatest { faceEnrolledAndEnabled ->
+ if (faceEnrolledAndEnabled) {
+ faceAcquired
+ } else {
+ emptyFlow()
+ }
+ }
+ .collect {
+ if (it.acquiredInfo == FaceManager.FACE_ACQUIRED_START) {
+ faceHelpMessageDeferral.reset()
+ }
+ faceHelpMessageDeferral.processFrame(it.acquiredInfo)
+ }
+ }
+
+ scope.launch {
+ biometricSettingsInteractor.faceAuthEnrolledAndEnabled
+ .flatMapLatest { faceEnrolledAndEnabled ->
+ if (faceEnrolledAndEnabled) {
+ faceHelp
+ } else {
+ emptyFlow()
+ }
+ }
+ .collect { helpAuthenticationStatus ->
+ helpAuthenticationStatus.msg?.let { msg ->
+ faceHelpMessageDeferral.updateMessage(helpAuthenticationStatus.msgId, msg)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
index 118215c6ba15..59c3f7f8aded 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
@@ -27,6 +27,7 @@ sealed class BiometricMessage(
/** Face biometric message */
open class FaceMessage(faceMessage: String?) : BiometricMessage(faceMessage)
+/** Face timeout message. */
data class FaceTimeoutMessage(
private val faceTimeoutMessage: String?,
) : FaceMessage(faceTimeoutMessage)
diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayExtensions.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayExtensions.kt
new file mode 100644
index 000000000000..0482bd8730f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/DisplayExtensions.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display
+
+import android.graphics.Rect
+import android.view.Display
+import android.view.DisplayInfo
+
+val Display.naturalBounds: Rect
+ get() {
+ val outDisplayInfo = DisplayInfo()
+ getDisplayInfo(outDisplayInfo)
+ return Rect(
+ /* left = */ 0,
+ /* top = */ 0,
+ /* right = */ outDisplayInfo.naturalWidth,
+ /* bottom = */ outDisplayInfo.naturalHeight
+ )
+ }
+
+val Display.naturalWidth: Int
+ get() {
+ val outDisplayInfo = DisplayInfo()
+ getDisplayInfo(outDisplayInfo)
+ return outDisplayInfo.naturalWidth
+ }
+
+val Display.naturalHeight: Int
+ get() {
+ val outDisplayInfo = DisplayInfo()
+ getDisplayInfo(outDisplayInfo)
+ return outDisplayInfo.naturalHeight
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index e914c503b4e8..33a69bf0d774 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -474,11 +474,6 @@ object Flags {
val WARN_ON_BLOCKING_BINDER_TRANSACTIONS =
unreleasedFlag("warn_on_blocking_binder_transactions")
- // TODO(b/283071711): Tracking bug
- @JvmField
- val TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK =
- unreleasedFlag("trim_resources_with_background_trim_on_lock")
-
// TODO:(b/283203305): Tracking bug
@JvmField val TRIM_FONT_CACHES_AT_UNLOCK = unreleasedFlag("trim_font_caches_on_unlock")
@@ -547,10 +542,6 @@ object Flags {
@JvmField
val ENABLE_NEW_PRIVACY_DIALOG = releasedFlag("enable_new_privacy_dialog")
- // TODO(b/302087895): Tracking Bug
- @JvmField val CALL_LAYOUT_ASYNC_SET_DATA =
- unreleasedFlag("call_layout_async_set_data", teamfood = true)
-
// TODO(b/302144438): Tracking Bug
@JvmField val DECOUPLE_REMOTE_INPUT_DELEGATE_AND_CALLBACK_UPDATE =
unreleasedFlag("decouple_remote_input_delegate_and_callback_update")
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index afcb03da39da..0bc29a8d0f16 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -122,6 +122,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.globalactions.domain.interactor.GlobalActionsInteractor;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.scrim.ScrimDrawable;
@@ -257,6 +258,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private final ShadeController mShadeController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final DialogTransitionAnimator mDialogTransitionAnimator;
+ private final GlobalActionsInteractor mInteractor;
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -368,7 +370,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
ShadeController shadeController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
DialogTransitionAnimator dialogTransitionAnimator,
- SelectedUserInteractor selectedUserInteractor) {
+ SelectedUserInteractor selectedUserInteractor,
+ GlobalActionsInteractor interactor) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -404,6 +407,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDialogTransitionAnimator = dialogTransitionAnimator;
mSelectedUserInteractor = selectedUserInteractor;
+ mInteractor = interactor;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -1333,6 +1337,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mUiEventLogger.log(GlobalActionsEvent.GA_POWER_MENU_CLOSE);
mWindowManagerFuncs.onGlobalActionsHidden();
mLifecycle.setCurrentState(Lifecycle.State.CREATED);
+ mInteractor.onDismissed();
}
/**
@@ -1342,6 +1347,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
public void onShow(DialogInterface dialog) {
mMetricsLogger.visible(MetricsEvent.POWER_MENU);
mUiEventLogger.log(GlobalActionsEvent.GA_POWER_MENU_OPEN);
+ mInteractor.onShown();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepository.kt b/packages/SystemUI/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepository.kt
new file mode 100644
index 000000000000..2550504cdc42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepository.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.globalactions.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Encapsulates application state for global actions. */
+@SysUISingleton
+class GlobalActionsRepository @Inject constructor() {
+ private val _isVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ /** Is the global actions dialog visible. */
+ val isVisible = _isVisible.asStateFlow()
+
+ /** Sets whether the global actions dialog is visible. */
+ fun setVisible(isVisible: Boolean) {
+ _isVisible.value = isVisible
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractor.kt
new file mode 100644
index 000000000000..c484a48a4058
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractor.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.globalactions.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.globalactions.data.repository.GlobalActionsRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+@SysUISingleton
+class GlobalActionsInteractor
+@Inject
+constructor(
+ private val repository: GlobalActionsRepository,
+) {
+ /** Is the global actions dialog visible. */
+ val isVisible: StateFlow<Boolean> = repository.isVisible
+
+ /** Notifies that the global actions dialog is shown. */
+ fun onShown() {
+ repository.setVisible(true)
+ }
+
+ /** Notifies that the global actions dialog has been dismissed. */
+ fun onDismissed() {
+ repository.setVisible(false)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
index c52ca68ee37f..e101b0ab64aa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
@@ -32,13 +32,13 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.utils.GlobalWindowManager
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
-import javax.inject.Inject
/**
* Releases cached resources on allocated by keyguard.
@@ -62,7 +62,7 @@ constructor(
override fun start() {
Log.d(LOG_TAG, "Resource trimmer registered.")
- if (featureFlags.isEnabled(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)) {
+ if (com.android.systemui.Flags.trimResourcesWithBackgroundTrimAtLock()) {
applicationScope.launch(bgDispatcher) {
// We need to wait for the AoD transition (and animation) to complete.
// This means we're waiting for isDreaming (== implies isDoze) and dozeAmount == 1f
@@ -107,19 +107,16 @@ constructor(
@WorkerThread
private fun onWakefulnessUpdated(
- isAsleep: Boolean,
- isDreaming: Boolean,
- isDozingFully: Boolean
+ isAsleep: Boolean,
+ isDreaming: Boolean,
+ isDozingFully: Boolean
) {
- if (!featureFlags.isEnabled(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)) {
+ if (!com.android.systemui.Flags.trimResourcesWithBackgroundTrimAtLock()) {
return
}
if (DEBUG) {
- Log.d(
- LOG_TAG,
- "isAsleep: $isAsleep Dreaming: $isDreaming DozeAmount: $isDozingFully"
- )
+ Log.d(LOG_TAG, "isAsleep: $isAsleep Dreaming: $isDreaming DozeAmount: $isDozingFully")
}
// There are three scenarios:
// * No dozing and no AoD at all - where we go directly to ASLEEP with isDreaming = false
@@ -129,8 +126,7 @@ constructor(
// * AoD - where we go to ASLEEP with iDreaming = true and dozeAmount slowly increases
// to 1f
val dozeDisabledAndScreenOff = isAsleep && !isDreaming
- val dozeEnabledAndDozeAnimationCompleted =
- isAsleep && isDreaming && isDozingFully
+ val dozeEnabledAndDozeAnimationCompleted = isAsleep && isDreaming && isDozingFully
if (dozeDisabledAndScreenOff || dozeEnabledAndDozeAnimationCompleted) {
Trace.beginSection("ResourceTrimmer#trimMemory")
Log.d(LOG_TAG, "SysUI asleep, trimming memory.")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
index 42f14f1eb317..9b3f13d16911 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
@@ -151,9 +151,13 @@ constructor(
awaitClose { revealAmountAnimator.removeUpdateListener(updateListener) }
}
+ private var willBeOrIsRevealed: Boolean? = null
+
override fun startRevealAmountAnimator(reveal: Boolean) {
+ if (reveal == willBeOrIsRevealed) return
+ willBeOrIsRevealed = reveal
if (reveal) revealAmountAnimator.start() else revealAmountAnimator.reverse()
- scrimLogger.d(TAG, "startRevealAmountAnimator, reveal", reveal)
+ scrimLogger.d(TAG, "startRevealAmountAnimator, reveal: ", reveal)
}
override val revealEffect =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index acbd9fb4c407..f45a9ecfaedf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -134,8 +134,9 @@ constructor(
powerInteractor.isAwake,
startedKeyguardTransitionStep,
keyguardInteractor.isKeyguardOccluded,
+ keyguardInteractor.isDreaming,
keyguardInteractor.isActiveDreamLockscreenHosted,
- communalInteractor.isIdleOnCommunal
+ communalInteractor.isIdleOnCommunal,
)
.collect {
(
@@ -143,6 +144,7 @@ constructor(
isAwake,
lastStartedTransitionStep,
occluded,
+ isDreaming,
isActiveDreamLockscreenHosted,
isIdleOnCommunal) ->
if (
@@ -152,10 +154,12 @@ constructor(
!isActiveDreamLockscreenHosted
) {
val toState =
- if (occluded) {
+ if (occluded && !isDreaming) {
KeyguardState.OCCLUDED
} else if (isIdleOnCommunal) {
KeyguardState.GLANCEABLE_HUB
+ } else if (isDreaming) {
+ KeyguardState.DREAMING
} else {
KeyguardState.LOCKSCREEN
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index c7f262a2ac80..4d731eccd9bb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -21,7 +21,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.ScreenPowerState
import com.android.systemui.statusbar.LightRevealEffect
@@ -53,11 +52,9 @@ constructor(
scope.launch {
transitionInteractor.startedKeyguardTransitionStep.collect {
scrimLogger.d(TAG, "listenForStartedKeyguardTransitionStep", it)
- if (willTransitionChangeEndState(it)) {
- lightRevealScrimRepository.startRevealAmountAnimator(
- willBeRevealedInState(it.to)
- )
- }
+ lightRevealScrimRepository.startRevealAmountAnimator(
+ willBeRevealedInState(it.to),
+ )
}
}
}
@@ -92,33 +89,25 @@ constructor(
companion object {
- /**
- * Whether the transition requires a change in the reveal amount of the light reveal scrim.
- * If not, we don't care about the transition and don't need to listen to it.
- */
- fun willTransitionChangeEndState(transition: TransitionStep): Boolean {
- return willBeRevealedInState(transition.from) != willBeRevealedInState(transition.to)
- }
-
- /**
- * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given
- * state after the transition is complete. If false, scrim will be fully hidden.
- */
- fun willBeRevealedInState(state: KeyguardState): Boolean {
- return when (state) {
- KeyguardState.OFF -> false
- KeyguardState.DOZING -> false
- KeyguardState.AOD -> false
- KeyguardState.DREAMING -> true
- KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true
- KeyguardState.GLANCEABLE_HUB -> true
- KeyguardState.ALTERNATE_BOUNCER -> true
- KeyguardState.PRIMARY_BOUNCER -> true
- KeyguardState.LOCKSCREEN -> true
- KeyguardState.GONE -> true
- KeyguardState.OCCLUDED -> true
- }
+ /**
+ * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given
+ * state after the transition is complete. If false, scrim will be fully hidden.
+ */
+ private fun willBeRevealedInState(state: KeyguardState): Boolean {
+ return when (state) {
+ KeyguardState.OFF -> false
+ KeyguardState.DOZING -> false
+ KeyguardState.AOD -> false
+ KeyguardState.DREAMING -> true
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true
+ KeyguardState.GLANCEABLE_HUB -> true
+ KeyguardState.ALTERNATE_BOUNCER -> true
+ KeyguardState.PRIMARY_BOUNCER -> true
+ KeyguardState.LOCKSCREEN -> true
+ KeyguardState.GONE -> true
+ KeyguardState.OCCLUDED -> true
}
+ }
val TAG = LightRevealScrimInteractor::class.simpleName!!
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index ed488487f524..dc1f33d53853 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -25,6 +25,7 @@ import android.graphics.Rect
import android.view.HapticFeedbackConstants
import android.view.View
import android.view.View.OnLayoutChangeListener
+import android.view.View.VISIBLE
import android.view.ViewGroup
import android.view.ViewGroup.OnHierarchyChangeListener
import android.view.ViewPropertyAnimator
@@ -66,6 +67,7 @@ import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
import javax.inject.Provider
+import kotlin.math.min
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
@@ -345,7 +347,7 @@ object KeyguardRootViewBinder {
}
}
- onLayoutChangeListener = OnLayoutChange(viewModel, burnInParams)
+ onLayoutChangeListener = OnLayoutChange(viewModel, childViews, burnInParams)
view.addOnLayoutChangeListener(onLayoutChangeListener)
// Views will be added or removed after the call to bind(). This is needed to avoid many
@@ -405,6 +407,7 @@ object KeyguardRootViewBinder {
private class OnLayoutChange(
private val viewModel: KeyguardRootViewModel,
+ private val childViews: Map<Int, View>,
private val burnInParams: MutableStateFlow<BurnInParameters>,
) : OnLayoutChangeListener {
override fun onLayoutChange(
@@ -418,7 +421,7 @@ object KeyguardRootViewBinder {
oldRight: Int,
oldBottom: Int
) {
- view.findViewById<View>(R.id.nssl_placeholder)?.let { notificationListPlaceholder ->
+ childViews[R.id.nssl_placeholder]?.let { notificationListPlaceholder ->
// After layout, ensure the notifications are positioned correctly
viewModel.onNotificationContainerBoundsChanged(
notificationListPlaceholder.top.toFloat(),
@@ -426,10 +429,34 @@ object KeyguardRootViewBinder {
)
}
- view.findViewById<View>(R.id.keyguard_status_view)?.let { statusView ->
- burnInParams.update { current -> current.copy(statusViewTop = statusView.top) }
+ burnInParams.update { current ->
+ current.copy(
+ minViewY =
+ if (migrateClocksToBlueprint()) {
+ // To ensure burn-in doesn't enroach the top inset, get the min top Y
+ childViews.entries.fold(Int.MAX_VALUE) { currentMin, (viewId, view) ->
+ min(
+ currentMin,
+ if (!isUserVisible(view)) {
+ Int.MAX_VALUE
+ } else {
+ view.getTop()
+ }
+ )
+ }
+ } else {
+ childViews[R.id.keyguard_status_view]?.top ?: 0
+ }
+ )
}
}
+
+ private fun isUserVisible(view: View): Boolean {
+ return view.id != R.id.burn_in_layer &&
+ view.visibility == VISIBLE &&
+ view.width > 0 &&
+ view.height > 0
+ }
}
suspend fun bindAodNotifIconVisibility(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
index 390b39f1e202..6e8605bde864 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
@@ -34,7 +34,7 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.keyguard.KeyguardViewConfigurator
import com.android.systemui.keyguard.shared.model.KeyguardSection
-import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.shade.NotificationPanelViewController
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
index d0f57c7f9ded..02c889dd771d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
@@ -111,7 +111,7 @@ constructor(
sceneContainerFlags,
controller,
notificationStackSizeCalculator,
- mainDispatcher,
+ mainImmediateDispatcher = mainDispatcher,
)
)
@@ -123,6 +123,7 @@ constructor(
notificationStackAppearanceViewModel,
ambientState,
controller,
+ mainImmediateDispatcher = mainDispatcher,
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
index b12a8a811955..21e945582aff 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
@@ -30,7 +30,7 @@ import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
-import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt
index 49c64bdcdd23..9edb4d159d4a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt
@@ -40,23 +40,17 @@ constructor(
alternateBouncerInteractor: AlternateBouncerInteractor,
) {
- private val faceHelp: Flow<FaceMessage> =
- biometricMessageInteractor.faceMessage.filterNot { faceMessage ->
- faceMessage !is FaceTimeoutMessage
- }
- private val fingerprintMessages: Flow<FingerprintMessage> =
- biometricMessageInteractor.fingerprintMessage.filterNot { fingerprintMessage ->
- // On lockout, the device will show the bouncer. Let's not show the message
- // before the transition or else it'll look flickery.
- fingerprintMessage is FingerprintLockoutMessage
- }
+ private val faceMessage: Flow<FaceMessage> =
+ biometricMessageInteractor.faceMessage.filterNot { it is FaceTimeoutMessage }
+ private val fingerprintMessage: Flow<FingerprintMessage> =
+ biometricMessageInteractor.fingerprintMessage.filterNot { it is FingerprintLockoutMessage }
val message: Flow<BiometricMessage?> =
alternateBouncerInteractor.isVisible.flatMapLatest { isVisible ->
if (isVisible) {
merge(
- faceHelp,
- fingerprintMessages,
+ faceMessage,
+ fingerprintMessage,
)
} else {
flowOf(null)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index 6fcbf48eab82..8665aabc3ef7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -158,9 +158,9 @@ constructor(
val burnInY = MathUtils.lerp(0, burnIn.translationY, interpolated).toInt()
val translationY =
if (Flags.migrateClocksToBlueprint()) {
- burnInY
+ max(params.topInset - params.minViewY, burnInY)
} else {
- max(params.topInset, params.statusViewTop + burnInY) - params.statusViewTop
+ max(params.topInset, params.minViewY + burnInY) - params.minViewY
}
BurnInModel(
@@ -194,8 +194,8 @@ data class BurnInParameters(
val clockControllerProvider: Provider<ClockController>? = null,
/** System insets that keyguard needs to stay out of */
val topInset: Int = 0,
- /** Status view top, without translation added in */
- val statusViewTop: Int = 0,
+ /** The min y-value of the visible elements on lockscreen */
+ val minViewY: Int = Int.MAX_VALUE,
/** The current y translation of the view */
val translationY: () -> Float? = { null }
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
index a0a77fbaee86..c40902871388 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -25,10 +25,13 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
/**
* Breaks down AOD->LOCKSCREEN transition into discrete steps for corresponding views to consume.
@@ -39,6 +42,7 @@ class AodToLockscreenTransitionViewModel
@Inject
constructor(
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+ shadeInteractor: ShadeInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
@@ -73,11 +77,22 @@ constructor(
}
val notificationAlpha: Flow<Float> =
- transitionAnimation.sharedFlow(
- duration = 500.milliseconds,
- onStep = { it },
- onCancel = { 1f },
- )
+ combine(
+ shadeInteractor.shadeExpansion.map { it > 0f },
+ shadeInteractor.qsExpansion.map { it > 0f },
+ transitionAnimation.sharedFlow(
+ duration = 500.milliseconds,
+ onStep = { it },
+ onCancel = { 1f },
+ ),
+ ) { isShadeExpanded, isQsExpanded, alpha ->
+ if (isShadeExpanded || isQsExpanded) {
+ // One example of this happening is dragging a notification while pulsing on AOD
+ 1f
+ } else {
+ alpha
+ }
+ }
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 35119338fb32..921eb66cd3cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.graphics.Point
+import android.util.MathUtils
import android.view.View.VISIBLE
import com.android.systemui.Flags.newAodTransition
import com.android.systemui.common.shared.model.NotificationContainerBounds
@@ -33,6 +34,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.ui.StateToValue
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -43,6 +45,7 @@ import com.android.systemui.util.ui.AnimatedValue
import com.android.systemui.util.ui.toAnimatedValueFlow
import com.android.systemui.util.ui.zip
import javax.inject.Inject
+import kotlin.math.max
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -63,7 +66,7 @@ constructor(
private val dozeParameters: DozeParameters,
private val keyguardInteractor: KeyguardInteractor,
private val communalInteractor: CommunalInteractor,
- keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
private val alternateBouncerToGoneTransitionViewModel:
AlternateBouncerToGoneTransitionViewModel,
@@ -87,6 +90,7 @@ constructor(
private val screenOffAnimationController: ScreenOffAnimationController,
private val aodBurnInViewModel: AodBurnInViewModel,
private val aodAlphaViewModel: AodAlphaViewModel,
+ private val shadeInteractor: ShadeInteractor,
) {
val burnInLayerVisibility: Flow<Int> =
@@ -102,6 +106,16 @@ constructor(
.onStart { emit(false) }
.distinctUntilChanged()
+ private val alphaOnShadeExpansion: Flow<Float> =
+ combine(
+ shadeInteractor.qsExpansion,
+ shadeInteractor.shadeExpansion,
+ ) { qsExpansion, shadeExpansion ->
+ // Fade out quickly as the shade expands
+ 1f - MathUtils.constrainedMap(0f, 1f, 0f, 0.2f, max(qsExpansion, shadeExpansion))
+ }
+ .distinctUntilChanged()
+
/** Last point that the root view was tapped */
val lastRootViewTapPosition: Flow<Point?> = keyguardInteractor.lastRootViewTapPosition
@@ -119,10 +133,12 @@ constructor(
fun alpha(viewState: ViewStateAccessor): Flow<Float> {
return combine(
communalInteractor.isIdleOnCommunal,
+ keyguardTransitionInteractor.transitionValue(GONE).onStart { emit(0f) },
// The transitions are mutually exclusive, so they are safe to merge to get the last
// value emitted by any of them. Do not add flows that cannot make this guarantee.
merge(
aodAlphaViewModel.alpha,
+ alphaOnShadeExpansion,
keyguardInteractor.dismissAlpha.filterNotNull(),
alternateBouncerToGoneTransitionViewModel.lockscreenAlpha,
aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
@@ -140,11 +156,12 @@ constructor(
primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha,
)
.onStart { emit(1f) }
- ) { isIdleOnCommunal, alpha ->
- if (isIdleOnCommunal) {
+ ) { isIdleOnCommunal, goneValue, alpha ->
+ if (isIdleOnCommunal || goneValue == 1f) {
// Keyguard should not show while the communal hub is fully visible. This check
// is added since at the moment, closing the notification shade will cause the
- // keyguard alpha to be set back to 1.
+ // keyguard alpha to be set back to 1. Also ensure keyguard is never visible
+ // when GONE.
0f
} else {
alpha
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index ac579d6d2491..cc3729b5b4d1 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -333,7 +333,7 @@ public class LogModule {
/**
* Provides a buffer for our connections and disconnections to MediaBrowserService.
*
- * See {@link com.android.systemui.media.controls.resume.ResumeMediaBrowser}.
+ * See {@link com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser}.
*/
@Provides
@SysUISingleton
@@ -345,7 +345,7 @@ public class LogModule {
/**
* Provides a buffer for updates to the media carousel.
*
- * See {@link com.android.systemui.media.controls.ui.MediaCarouselController}.
+ * See {@link com.android.systemui.media.controls.ui.controller.MediaCarouselController}.
*/
@Provides
@SysUISingleton
@@ -637,4 +637,11 @@ public class LogModule {
return factory.create("NavBarButtonClick", 50);
}
+ /** Provides a {@link LogBuffer} for NavBar Orientation Tracking. */
+ @Provides
+ @SysUISingleton
+ @NavbarOrientationTrackingLog
+ public static LogBuffer provideNavbarOrientationTrackingLogBuffer(LogBufferFactory factory) {
+ return factory.create("NavbarOrientationTrackingLog", 50);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java
index 1c00c93f4e38..901559bd3d8b 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java
@@ -26,7 +26,8 @@ import java.lang.annotation.Retention;
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for {@link com.android.systemui.media.controls.resume.ResumeMediaBrowser}
+ * A {@link LogBuffer} for
+ * {@link com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser}
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java
index 86a916ef6541..abbfd4fa32ed 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java
@@ -26,7 +26,8 @@ import java.lang.annotation.Retention;
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for {@link com.android.systemui.media.controls.ui.MediaCarouselController}
+ * A {@link LogBuffer} for
+ * {@link com.android.systemui.media.controls.ui.controller.MediaCarouselController}
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java
index 98e6556d7f53..0239caa39544 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java
@@ -26,7 +26,8 @@ import java.lang.annotation.Retention;
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for {@link com.android.systemui.media.controls.pipeline.MediaTimeoutLogger}
+ * A {@link LogBuffer} for
+ * {@link com.android.systemui.media.controls.domain.pipeline.MediaTimeoutLogger}
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java
index dde0ee0796d4..27a6a64b1302 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java
@@ -26,7 +26,7 @@ import java.lang.annotation.Retention;
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for {@link com.android.systemui.media.controls.ui.MediaViewLogger}
+ * A {@link LogBuffer} for {@link com.android.systemui.media.controls.ui.controller.MediaViewLogger}
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NavbarOrientationTrackingLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NavbarOrientationTrackingLog.java
new file mode 100644
index 000000000000..46790a66cbb6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NavbarOrientationTrackingLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for {@link com.android.systemui.navigationbar.NavigationBar}. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface NavbarOrientationTrackingLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
index 789ef407ea9d..ad70db5a3300 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import javax.inject.Inject
/** Combines [MediaDataManager.Listener] events with [MediaDeviceManager.Listener] events. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilter.kt
index 185a78369a9e..bc539efdfe69 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilter.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.content.Context
import android.content.pm.UserInfo
@@ -24,9 +24,9 @@ import com.android.internal.annotations.KeepForWeakReference
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_RESUME
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_RESUME
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.settings.UserTracker
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
index 47df3b79b8bb..6fc22ea60a9e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.annotation.SuppressLint
import android.app.BroadcastOptions
@@ -66,17 +66,17 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.models.player.MediaAction
-import com.android.systemui.media.controls.models.player.MediaButton
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_SOURCE
-import com.android.systemui.media.controls.models.recommendation.EXTRA_VALUE_TRIGGER_PERIODIC
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaDataProvider
-import com.android.systemui.media.controls.resume.MediaResumeListener
-import com.android.systemui.media.controls.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.domain.resume.MediaResumeListener
+import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
+import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
+import com.android.systemui.media.controls.shared.model.MediaAction
+import com.android.systemui.media.controls.shared.model.MediaButton
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.media.controls.util.MediaFlags
@@ -583,6 +583,10 @@ class MediaDataManager(
}
return
}
+ // Update last active if media was still active.
+ if (it.active) {
+ it.lastActive = systemClock.elapsedRealtime()
+ }
it.active = !timedOut
if (DEBUG) Log.d(TAG, "Updating $key timedOut: $timedOut")
onMediaDataLoaded(key, key, it)
@@ -1520,6 +1524,12 @@ class MediaDataManager(
context.packageManager.getLaunchIntentForPackage(data.packageName)?.let {
PendingIntent.getActivity(context, 0, it, PendingIntent.FLAG_IMMUTABLE)
}
+ val lastActive =
+ if (data.active) {
+ systemClock.elapsedRealtime()
+ } else {
+ data.lastActive
+ }
val updated =
data.copy(
token = null,
@@ -1531,6 +1541,7 @@ class MediaDataManager(
isPlaying = false,
isClearable = true,
clickIntent = launcherIntent,
+ lastActive = lastActive,
)
val pkg = data.packageName
val migrate = mediaEntries.put(pkg, updated) == null
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index dcbf670460ef..f4d70a5e78c9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.bluetooth.BluetoothLeBroadcast
import android.bluetooth.BluetoothLeBroadcastMetadata
@@ -30,6 +30,8 @@ import androidx.annotation.MainThread
import androidx.annotation.WorkerThread
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.flags.Flags.enableLeAudioSharing
+import com.android.settingslib.flags.Flags.legacyLeAudioSharing
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.PhoneMediaDevice
@@ -37,8 +39,9 @@ import com.android.systemui.Dumpable
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.util.LocalMediaManagerFactory
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager
@@ -331,14 +334,28 @@ constructor(
@WorkerThread
private fun updateCurrent() {
if (isLeAudioBroadcastEnabled()) {
- current =
- MediaDeviceData(
- /* enabled */ true,
- /* icon */ context.getDrawable(R.drawable.settings_input_antenna),
- /* name */ broadcastDescription,
- /* intent */ null,
- /* showBroadcastButton */ showBroadcastButton = true
- )
+ if (enableLeAudioSharing()) {
+ current =
+ MediaDeviceData(
+ enabled = false,
+ icon =
+ context.getDrawable(
+ com.android.settingslib.R.drawable.ic_bt_le_audio_sharing
+ ),
+ name = context.getString(R.string.audio_sharing_description),
+ intent = null,
+ showBroadcastButton = false
+ )
+ } else {
+ current =
+ MediaDeviceData(
+ /* enabled */ true,
+ /* icon */ context.getDrawable(R.drawable.settings_input_antenna),
+ /* name */ broadcastDescription,
+ /* intent */ null,
+ /* showBroadcastButton */ showBroadcastButton = true
+ )
+ }
} else {
val aboutToConnect = aboutToConnectDeviceOverride
if (
@@ -419,6 +436,7 @@ constructor(
@WorkerThread
private fun isLeAudioBroadcastEnabled(): Boolean {
+ if (!enableLeAudioSharing() && !legacyLeAudioSharing()) return false
val localBluetoothManager = localBluetoothManager.get()
if (localBluetoothManager != null) {
val profileManager = localBluetoothManager.profileManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
index 6a8ffb7d8c42..b2a8f2e71cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.content.ComponentName
import android.content.Context
@@ -25,8 +25,8 @@ import android.media.session.MediaSessionManager
import android.util.Log
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -199,9 +199,7 @@ constructor(
packageControllers.put(controller.packageName, tokens)
}
}
- controllers?.map { TokenId(it.sessionToken) }?.let {
- tokensWithNotifications.retainAll(it)
- }
+ controllers?.map { TokenId(it.sessionToken) }?.let { tokensWithNotifications.retainAll(it) }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
index ed4eef9eaa2b..29f396700831 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.media.session.MediaController
import android.media.session.MediaSession
@@ -23,8 +23,8 @@ import android.os.SystemProperties
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutLogger.kt
index 534241edb253..c50c46a853a5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.media.session.PlaybackState
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaBrowserFactory.java
index 00620b5b2575..2c45ddb5542a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaBrowserFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaBrowserFactory.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume;
+package com.android.systemui.media.controls.domain.resume;
import android.content.ComponentName;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
index 23ee00d88fdc..e4047e5f96c5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume
+package com.android.systemui.media.controls.domain.resume
import android.content.BroadcastReceiver
import android.content.ComponentName
@@ -34,9 +34,9 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.settings.UserTracker
import com.android.systemui.tuner.TunerService
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowser.java
index ceaccafd8f40..b2960cd287c6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowser.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowser.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume;
+package com.android.systemui.media.controls.domain.resume;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserFactory.java
index e37419127f5b..50eb77642632 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserFactory.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume;
+package com.android.systemui.media.controls.domain.resume;
import android.annotation.UserIdInt;
import android.content.ComponentName;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserLogger.kt
index 888b9c7cc901..ce2a0803bbfb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume
+package com.android.systemui.media.controls.domain.resume
import android.content.ComponentName
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
index 5caa27f02bd3..4fa7cb54431f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.shared.model
import android.app.PendingIntent
import android.graphics.drawable.Drawable
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaData.kt
index ae03f27b32cd..52c605f55665 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaData.kt
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.systemui.media.controls.models.recommendation
+package com.android.systemui.media.controls.shared.model
import android.app.smartspace.SmartspaceAction
import android.content.Context
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaDataProvider.kt
index cacb3e2bbe4d..8726d8193800 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaDataProvider.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.recommendation
+package com.android.systemui.media.controls.shared.model
import android.app.smartspace.SmartspaceTarget
import android.util.Log
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandler.kt
index f5cc04331f94..82580590a11c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandler.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.graphics.drawable.Animatable2
import android.graphics.drawable.Drawable
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
index 952f9b8711f0..21407f3bd6d4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
@@ -27,7 +27,7 @@ import android.graphics.drawable.RippleDrawable
import com.android.internal.R
import com.android.internal.annotations.VisibleForTesting
import com.android.settingslib.Utils
-import com.android.systemui.media.controls.models.player.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.monet.ColorScheme
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect
import com.android.systemui.surfaceeffects.ripple.MultiRippleController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaColorSchemes.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MediaColorSchemes.kt
index 2a8362b64cd6..3c57c83ff9fe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaColorSchemes.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MediaColorSchemes.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import com.android.systemui.monet.ColorScheme
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MetadataAnimationHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandler.kt
index 1cdcf5ed2702..98202c51706a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MetadataAnimationHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandler.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
index 8d918e76b1e1..34f7c4dcaec0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.binder
import android.animation.Animator
import android.animation.ObjectAnimator
@@ -24,7 +24,9 @@ import androidx.lifecycle.Observer
import com.android.app.animation.Interpolators
import com.android.app.tracing.TraceStateLogger
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.media.controls.ui.SquigglyProgress
+import com.android.systemui.media.controls.ui.drawable.SquigglyProgress
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.res.R
private const val TAG = "SeekBarObserver"
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
index c629337cad35..9206af28eeff 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.content.Context
import android.content.res.Configuration
@@ -31,6 +31,8 @@ import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.dagger.MediaModule.KEYGUARD
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt
index 0dd4b580f95e..c0d9dc23a6d5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.view.ViewGroup
import com.android.systemui.log.LogBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 992eeca77e54..b721236eab01 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.app.PendingIntent
import android.content.Context
@@ -46,12 +46,15 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.ui.MediaControlPanel.SMARTSPACE_CARD_DISMISS_EVENT
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.controller.MediaControlPanel.SMARTSPACE_CARD_DISMISS_EVENT
+import com.android.systemui.media.controls.ui.view.MediaCarouselScrollHandler
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaScrollView
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.media.controls.util.SmallHash
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
index 3dc00004e900..ebf1c6a10703 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 7cf18bafa153..e8ad4d325591 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui;
+package com.android.systemui.media.controls.ui.controller;
import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS;
import static com.android.systemui.Flags.legacyLeAudioSharing;
-import static com.android.systemui.media.controls.models.recommendation.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;
+import static com.android.systemui.media.controls.shared.model.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;
import android.animation.Animator;
import android.animation.AnimatorInflater;
@@ -90,17 +90,21 @@ import com.android.systemui.bluetooth.BroadcastDialogController;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.media.controls.models.GutsViewHolder;
-import com.android.systemui.media.controls.models.player.MediaAction;
-import com.android.systemui.media.controls.models.player.MediaButton;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.models.player.MediaDeviceData;
-import com.android.systemui.media.controls.models.player.MediaViewHolder;
-import com.android.systemui.media.controls.models.player.SeekBarObserver;
-import com.android.systemui.media.controls.models.player.SeekBarViewModel;
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder;
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.shared.model.MediaAction;
+import com.android.systemui.media.controls.shared.model.MediaButton;
+import com.android.systemui.media.controls.shared.model.MediaData;
+import com.android.systemui.media.controls.shared.model.MediaDeviceData;
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData;
+import com.android.systemui.media.controls.ui.animation.AnimationBindHandler;
+import com.android.systemui.media.controls.ui.animation.ColorSchemeTransition;
+import com.android.systemui.media.controls.ui.animation.MediaColorSchemesKt;
+import com.android.systemui.media.controls.ui.animation.MetadataAnimationHandler;
+import com.android.systemui.media.controls.ui.binder.SeekBarObserver;
+import com.android.systemui.media.controls.ui.view.GutsViewHolder;
+import com.android.systemui.media.controls.ui.view.MediaViewHolder;
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder;
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel;
import com.android.systemui.media.controls.util.MediaDataUtils;
import com.android.systemui.media.controls.util.MediaFlags;
import com.android.systemui.media.controls.util.MediaUiEventLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index 35e0271c1b8f..3b989d935cbd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
@@ -42,7 +42,8 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManager.kt
index 1f711cfdd966..8660d12bcb85 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManager.kt
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import com.android.app.tracing.traceSection
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.util.animation.MeasurementOutput
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 962764c028fc..ad7990b92931 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.content.Context
import android.content.res.Configuration
@@ -22,10 +22,11 @@ import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
import com.android.app.tracing.traceSection
-import com.android.systemui.media.controls.models.GutsViewHolder
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.calculateAlpha
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController.Companion.calculateAlpha
+import com.android.systemui.media.controls.ui.view.GutsViewHolder
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
index 3ff2315956ad..1514db38d882 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/IlluminationDrawable.kt
index 5aa6824a98b1..260d30061296 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/IlluminationDrawable.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.drawable
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/LightSourceDrawable.kt
index 6ee072d41ecc..e4ce1353d30f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/LightSourceDrawable.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.drawable
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgress.kt
index 47df021eaf83..c417fe60219a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgress.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.drawable
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/GutsViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/GutsViewHolder.kt
index f5f5d388a278..a667c5819062 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/GutsViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/GutsViewHolder.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models
+package com.android.systemui.media.controls.ui.view
import android.content.res.ColorStateList
import android.util.Log
@@ -22,11 +22,11 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.TextView
-import com.android.systemui.res.R
-import com.android.systemui.media.controls.ui.accentPrimaryFromScheme
-import com.android.systemui.media.controls.ui.surfaceFromScheme
-import com.android.systemui.media.controls.ui.textPrimaryFromScheme
+import com.android.systemui.media.controls.ui.animation.accentPrimaryFromScheme
+import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
+import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
import com.android.systemui.monet.ColorScheme
+import com.android.systemui.res.R
/**
* A view holder for the guts menu of a media player. The guts are shown when the user long-presses
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
index 038582c4e999..c033e466d7d7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.view
import android.graphics.Outline
import android.util.MathUtils
@@ -31,6 +31,7 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.settingslib.Utils
import com.android.systemui.Gefingerpoken
import com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS
+import com.android.systemui.media.controls.ui.controller.MediaControlPanel
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
index 437218f9f440..d92168bf9fa4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
@@ -14,15 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.view
import android.graphics.Rect
import android.util.ArraySet
import android.view.View
import android.view.View.OnAttachStateChangeListener
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHostStatesManager
+import com.android.systemui.media.controls.ui.controller.MediaLocation
import com.android.systemui.util.animation.DisappearParameters
import com.android.systemui.util.animation.MeasurementInput
import com.android.systemui.util.animation.MeasurementOutput
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaScrollView.kt
index 10512f1c4aed..b6259081e174 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaScrollView.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.view
import android.content.Context
import android.os.SystemClock
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaViewHolder.kt
index 898eacff6246..35309ea65a4c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaViewHolder.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.view
import android.view.LayoutInflater
import android.view.View
@@ -25,7 +25,6 @@ import android.widget.SeekBar
import android.widget.TextView
import androidx.constraintlayout.widget.Barrier
import com.android.internal.widget.CachingIconView
-import com.android.systemui.media.controls.models.GutsViewHolder
import com.android.systemui.res.R
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
import com.android.systemui.surfaceeffects.ripple.MultiRippleView
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt
index 8ac8a2da4478..2d028d0213ff 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.recommendation
+package com.android.systemui.media.controls.ui.view
import android.view.LayoutInflater
import android.view.View
@@ -23,9 +23,8 @@ import android.widget.ImageView
import android.widget.SeekBar
import android.widget.TextView
import com.android.internal.widget.CachingIconView
+import com.android.systemui.media.controls.ui.drawable.IlluminationDrawable
import com.android.systemui.res.R
-import com.android.systemui.media.controls.models.GutsViewHolder
-import com.android.systemui.media.controls.ui.IlluminationDrawable
import com.android.systemui.util.animation.TransitionLayout
private const val TAG = "RecommendationViewHolder"
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
index 13d743f8a4e4..cef1e69e7b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.viewmodel
import android.media.MediaMetadata
import android.media.session.MediaController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/LocalMediaManagerFactory.kt
index 1d3cfd2569aa..5d113a97c42f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/LocalMediaManagerFactory.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.util
import android.content.Context
import com.android.settingslib.bluetooth.LocalBluetoothManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
index 16a703a6bfdd..f8c816ca0b52 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
@@ -21,9 +21,9 @@ import com.android.internal.logging.InstanceIdSequence
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.MediaLocation
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaLocation
import com.android.systemui.res.R
import java.lang.IllegalArgumentException
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 8f752e59e806..d84e5dde6967 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -19,10 +19,10 @@ package com.android.systemui.media.dagger;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogBufferFactory;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
-import com.android.systemui.media.controls.ui.MediaHost;
-import com.android.systemui.media.controls.ui.MediaHostStatesManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
+import com.android.systemui.media.controls.ui.controller.MediaHostStatesManager;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.media.dream.dagger.MediaComplicationComponent;
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.media.taptotransfer.MediaTttFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java
index b4153d72e64c..7f6398be526d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java
@@ -21,9 +21,9 @@ import static com.android.systemui.media.dream.dagger.MediaComplicationComponent
import android.widget.FrameLayout;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
-import com.android.systemui.media.controls.ui.MediaHost;
-import com.android.systemui.media.controls.ui.MediaHostState;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
+import com.android.systemui.media.controls.ui.view.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHostState;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
index 08c626c9e0eb..88a5f78e407d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
@@ -27,9 +27,9 @@ import com.android.systemui.CoreStartable;
import com.android.systemui.complication.DreamMediaEntryComplication;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.shared.model.MediaData;
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavbarOrientationTrackingLogger.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NavbarOrientationTrackingLogger.kt
new file mode 100644
index 000000000000..b1bd2863695a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavbarOrientationTrackingLogger.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar
+
+import android.view.Surface
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.NavbarOrientationTrackingLog
+import javax.inject.Inject
+
+class NavbarOrientationTrackingLogger
+@Inject
+constructor(@NavbarOrientationTrackingLog private val buffer: LogBuffer) {
+ fun logPrimaryAndSecondaryVisibility(
+ methodName: String,
+ isViewVisible: Boolean,
+ isImmersiveMode: Boolean,
+ isSecondaryHandleVisible: Boolean,
+ currentRotation: Int,
+ startingQuickSwitchRotation: Int
+ ) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = methodName
+ bool1 = isViewVisible
+ bool2 = isImmersiveMode
+ bool3 = isSecondaryHandleVisible
+ int1 = startingQuickSwitchRotation
+ int2 = currentRotation
+ },
+ {
+ "Caller Method: $str1\n" +
+ "\tNavbar Visible: $bool1\n" +
+ "\tImmersive Mode: $bool2\n" +
+ "\tSecondary Handle Visible: $bool3\n" +
+ "\tDelta Rotation: ${getDeltaRotation(int1, int2)}\n" +
+ "\tStarting QuickSwitch Rotation: $int1\n" +
+ "\tCurrent Rotation: $int2\n"
+ }
+ )
+ }
+
+ private fun getDeltaRotation(oldRotation: Int, newRotation: Int): String {
+ var rotation: String = "0"
+ when (deltaRotation(oldRotation, newRotation)) {
+ Surface.ROTATION_90 -> {
+ rotation = "90"
+ }
+ Surface.ROTATION_180 -> {
+ rotation = "180"
+ }
+ Surface.ROTATION_270 -> {
+ rotation = "270"
+ }
+ }
+ return rotation
+ }
+
+ private fun deltaRotation(oldRotation: Int, newRotation: Int): Int {
+ var delta = newRotation - oldRotation
+ if (delta < 0) delta += 4
+ return delta
+ }
+}
+
+private const val TAG = "NavbarOrientationTracking"
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 95b75ac7a875..f4903f1f054f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -282,6 +282,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private final Rect mSamplingBounds = new Rect();
private final Binder mInsetsSourceOwner = new Binder();
private final NavBarButtonClickLogger mNavBarButtonClickLogger;
+ private final NavbarOrientationTrackingLogger mNavbarOrientationTrackingLogger;
/**
* When quickswitching between apps of different orientations, we draw a secondary home handle
@@ -557,7 +558,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
WakefulnessLifecycle wakefulnessLifecycle,
TaskStackChangeListeners taskStackChangeListeners,
DisplayTracker displayTracker,
- NavBarButtonClickLogger navBarButtonClickLogger) {
+ NavBarButtonClickLogger navBarButtonClickLogger,
+ NavbarOrientationTrackingLogger navbarOrientationTrackingLogger) {
super(navigationBarView);
mFrame = navigationBarFrame;
mContext = context;
@@ -600,6 +602,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mDisplayTracker = displayTracker;
mEdgeBackGestureHandler = navBarHelper.getEdgeBackGestureHandler();
mNavBarButtonClickLogger = navBarButtonClickLogger;
+ mNavbarOrientationTrackingLogger = navbarOrientationTrackingLogger;
mNavColorSampleMargin = getResources()
.getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
@@ -906,6 +909,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
| WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
mWindowManager.addView(mOrientationHandle, mOrientationParams);
mOrientationHandle.setVisibility(View.GONE);
+
+ logNavbarOrientation("initSecondaryHomeHandleForRotation");
mOrientationParams.setFitInsetsTypes(0 /* types*/);
mOrientationHandleGlobalLayoutListener =
() -> {
@@ -966,6 +971,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mWindowManager.updateViewLayout(mOrientationHandle, mOrientationParams);
mView.setVisibility(View.GONE);
mOrientationHandle.setVisibility(View.VISIBLE);
+ logNavbarOrientation("orientSecondaryHomeHandle");
}
private void resetSecondaryHandle() {
@@ -975,9 +981,23 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mOrientationHandle.setVisibility(View.GONE);
}
mView.setVisibility(View.VISIBLE);
+ logNavbarOrientation("resetSecondaryHandle");
setOrientedHandleSamplingRegion(null);
}
+ /**
+ * Logging method for issues concerning Navbar/secondary handle visibility.
+ */
+ private void logNavbarOrientation(String methodName) {
+ boolean isViewVisible = (mView != null) && (mView.getVisibility() == View.VISIBLE);
+ boolean isSecondaryHandleVisible =
+ (mOrientationHandle != null) && (mOrientationHandle.getVisibility()
+ == View.VISIBLE);
+ mNavbarOrientationTrackingLogger.logPrimaryAndSecondaryVisibility(methodName, isViewVisible,
+ mShowOrientedHandleForImmersiveMode, isSecondaryHandleVisible, mCurrentRotation,
+ mStartingQuickSwitchRotation);
+ }
+
private void parseCurrentSysuiState() {
NavBarHelper.CurrentSysuiState state = mNavBarHelper.getCurrentSysuiState();
if (state.mWindowStateDisplayId == mDisplayId) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index e5d970d3b40a..741336277119 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -51,7 +51,7 @@ import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index c3f5086b0096..2440651555d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -27,9 +27,9 @@ import android.view.View;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
-import com.android.systemui.media.controls.ui.MediaHost;
-import com.android.systemui.media.controls.ui.MediaHostState;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
+import com.android.systemui.media.controls.ui.view.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHostState;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 1c37510fde34..975c871bd006 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -32,7 +32,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizerController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index f278dce047e0..a8e88da5d288 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -25,8 +25,8 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
index 4bad45f19673..16aa99e22ae2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
@@ -25,6 +25,8 @@ import com.android.systemui.qs.pipeline.data.repository.DefaultTilesQSHostReposi
import com.android.systemui.qs.pipeline.data.repository.DefaultTilesRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepositoryImpl
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesResourceRepository
import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredBroadcastRepository
import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
@@ -81,6 +83,11 @@ abstract class QSPipelineModule {
impl: QSSettingsRestoredBroadcastRepository
): QSSettingsRestoredRepository
+ @Binds
+ abstract fun provideMinimumTilesRepository(
+ impl: MinimumTilesResourceRepository
+ ): MinimumTilesRepository
+
companion object {
/**
* Provides a logging buffer for all logs related to the new Quick Settings pipeline to log
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesRepository.kt
new file mode 100644
index 000000000000..3a005c0ebfed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesRepository.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.content.res.Resources
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/**
+ * Provides the minimum number of tiles required in QS. The default number of tiles should be at
+ * least this many.
+ */
+interface MinimumTilesRepository {
+ val minNumberOfTiles: Int
+}
+
+/**
+ * Minimum number of tiles using the corresponding resource. The value will be read once upon
+ * creation, as it's not expected to change.
+ */
+@SysUISingleton
+class MinimumTilesResourceRepository @Inject constructor(@Main resources: Resources) :
+ MinimumTilesRepository {
+ override val minNumberOfTiles: Int =
+ resources.getInteger(R.integer.quick_settings_min_num_tiles)
+}
+
+/** Provides a fixed minimum number of tiles. */
+class MinimumTilesFixedRepository(override val minNumberOfTiles: Int = 0) : MinimumTilesRepository
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
index e718eea09665..a2bb9e9a2f63 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
@@ -6,6 +6,8 @@ import android.os.UserHandle
import android.provider.Settings
import android.util.Log
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.common.shared.model.PackageChangeModel.Empty.user
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -13,18 +15,28 @@ import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository.Companion.BUFFER_CAPACITY
import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter.toTilesList
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.util.kotlin.emitOnStart
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flattenConcat
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
/** Provides restored data (from Backup and Restore) for Quick Settings pipeline */
interface QSSettingsRestoredRepository {
@@ -44,34 +56,87 @@ class QSSettingsRestoredBroadcastRepository
@Inject
constructor(
broadcastDispatcher: BroadcastDispatcher,
+ private val deviceProvisionedController: DeviceProvisionedController,
logger: QSPipelineLogger,
@Application private val scope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : QSSettingsRestoredRepository {
+ private val onUserSetupChangedForSomeUser =
+ conflatedCallbackFlow {
+ val callback =
+ object : DeviceProvisionedController.DeviceProvisionedListener {
+ override fun onUserSetupChanged() {
+ trySend(Unit)
+ }
+ }
+ deviceProvisionedController.addCallback(callback)
+ awaitClose { deviceProvisionedController.removeCallback(callback) }
+ }
+ .emitOnStart()
+
+ @OptIn(ExperimentalCoroutinesApi::class)
override val restoreData =
- flow {
+ run {
+ val mutex = Mutex()
val firstIntent = mutableMapOf<Int, Intent>()
- broadcastDispatcher
- .broadcastFlow(INTENT_FILTER, UserHandle.ALL) { intent, receiver ->
- intent to receiver.sendingUserId
- }
- .filter { it.first.isCorrectSetting() }
- .collect { (intent, user) ->
- if (user !in firstIntent) {
- firstIntent[user] = intent
- } else {
- val firstRestored = firstIntent.remove(user)!!
- emit(processIntents(user, firstRestored, intent))
+
+ val restoresFromTwoBroadcasts: Flow<RestoreData> =
+ broadcastDispatcher
+ .broadcastFlow(INTENT_FILTER, UserHandle.ALL) { intent, receiver ->
+ intent to receiver.sendingUserId
}
- }
+ .filter { it.first.isCorrectSetting() }
+ .mapNotNull { (intent, user) ->
+ mutex.withLock {
+ if (user !in firstIntent) {
+ firstIntent[user] = intent
+ null
+ } else {
+ val firstRestored = firstIntent.remove(user)!!
+ processIntents(user, firstRestored, intent)
+ }
+ }
+ }
+ .catch { Log.e(TAG, "Error parsing broadcast", it) }
+
+ val restoresFromUserSetup: Flow<RestoreData> =
+ onUserSetupChangedForSomeUser
+ .map {
+ mutex.withLock {
+ firstIntent
+ .filter { (userId, _) ->
+ deviceProvisionedController.isUserSetup(userId)
+ }
+ .onEach { firstIntent.remove(it.key) }
+ .map { processSingleIntent(it.key, it.value) }
+ .asFlow()
+ }
+ }
+ .flattenConcat()
+ .catch { Log.e(TAG, "Error parsing tiles intent after user setup", it) }
+ .onEach { logger.logSettingsRestoredOnUserSetupComplete(it.userId) }
+ merge(restoresFromTwoBroadcasts, restoresFromUserSetup)
}
- .catch { Log.e(TAG, "Error parsing broadcast", it) }
.flowOn(backgroundDispatcher)
.buffer(BUFFER_CAPACITY)
.shareIn(scope, SharingStarted.Eagerly)
.onEach(logger::logSettingsRestored)
+ private fun processSingleIntent(user: Int, intent: Intent): RestoreData {
+ intent.validateIntent()
+ if (intent.getStringExtra(Intent.EXTRA_SETTING_NAME) != TILES_SETTING) {
+ throw IllegalStateException(
+ "Single intent restored for user $user is not tiles: $intent"
+ )
+ }
+ return RestoreData(
+ (intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE) ?: "").toTilesList(),
+ emptySet(),
+ user,
+ )
+ }
+
private fun processIntents(user: Int, intent1: Intent, intent2: Intent): RestoreData {
intent1.validateIntent()
intent2.validateIntent()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
index 00ea0b5c5ed3..214e9f097642 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
@@ -19,12 +19,12 @@ package com.android.systemui.qs.pipeline.data.repository
import android.annotation.UserIdInt
import android.content.res.Resources
import android.util.SparseArray
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.res.R
import com.android.systemui.retail.data.repository.RetailModeRepository
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -68,6 +68,9 @@ interface TileSpecRepository {
suspend fun reconcileRestore(restoreData: RestoreData, currentAutoAdded: Set<TileSpec>)
+ /** Prepend the default list of tiles to the current set of tiles */
+ suspend fun prependDefault(@UserIdInt userId: Int)
+
companion object {
/** Position to indicate the end of the list */
const val POSITION_AT_END = -1
@@ -152,6 +155,12 @@ constructor(
?.reconcileRestore(restoreData, currentAutoAdded)
}
+ override suspend fun prependDefault(
+ userId: Int,
+ ) {
+ userTileRepositories.get(userId)?.prependDefault()
+ }
+
companion object {
private const val DELIMITER = TilesSettingConverter.DELIMITER
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
index 152fd0f83811..8ad5cb2c0a34 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
@@ -50,9 +50,8 @@ constructor(
private val defaultTiles: List<TileSpec>
get() = defaultTilesRepository.defaultTiles
- private val changeEvents = MutableSharedFlow<ChangeAction>(
- extraBufferCapacity = CHANGES_BUFFER_SIZE
- )
+ private val changeEvents =
+ MutableSharedFlow<ChangeAction>(extraBufferCapacity = CHANGES_BUFFER_SIZE)
private lateinit var _tiles: StateFlow<List<TileSpec>>
@@ -163,6 +162,10 @@ constructor(
changeEvents.emit(RestoreTiles(restoreData, currentAutoAdded))
}
+ suspend fun prependDefault() {
+ changeEvents.emit(PrependDefault(defaultTiles))
+ }
+
sealed interface ChangeAction {
fun apply(currentTiles: List<TileSpec>): List<TileSpec>
}
@@ -199,6 +202,12 @@ constructor(
}
}
+ private data class PrependDefault(val defaultTiles: List<TileSpec>) : ChangeAction {
+ override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+ return defaultTiles + currentTiles
+ }
+ }
+
private data class RestoreTiles(
val restoreData: RestoreData,
val currentAutoAdded: Set<TileSpec>,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
index b22119966460..3f619c08261d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
@@ -64,7 +64,8 @@ constructor(
fun maybeSend(profiles: List<UserInfo>) {
if (profiles.any { it.id == userId }) {
// We are looking at the profiles of the correct user.
- if (profiles.any { it.isManagedProfile }) {
+ // They need to be a managed enabled profile.
+ if (profiles.any { it.isManagedProfile && it.isEnabled }) {
trySend(
AutoAddSignal.Add(
spec,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 957cb1eb95d1..61896f0a3816 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -35,6 +35,7 @@ import com.android.systemui.qs.external.TileLifecycleManager
import com.android.systemui.qs.external.TileServiceKey
import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
import com.android.systemui.qs.pipeline.domain.model.TileModel
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
@@ -131,6 +132,7 @@ constructor(
private val tileSpecRepository: TileSpecRepository,
private val installedTilesComponentRepository: InstalledTilesComponentRepository,
private val userRepository: UserRepository,
+ private val minimumTilesRepository: MinimumTilesRepository,
private val customTileStatePersister: CustomTileStatePersister,
private val newQSTileFactory: Lazy<NewQSTileFactory>,
private val tileFactory: QSFactory,
@@ -255,17 +257,23 @@ constructor(
val resolvedSpecs = newTileMap.keys.toList()
specsToTiles.clear()
specsToTiles.putAll(newTileMap)
- _currentSpecsAndTiles.value =
+ val newResolvedTiles =
newTileMap
.filter { it.value is TileOrNotInstalled.Tile }
.map {
TileModel(it.key, (it.value as TileOrNotInstalled.Tile).tile)
}
+
+ _currentSpecsAndTiles.value = newResolvedTiles
logger.logTilesNotInstalled(
newTileMap.filter { it.value is TileOrNotInstalled.NotInstalled }.keys,
newUser
)
- if (resolvedSpecs != newTileList) {
+ if (newResolvedTiles.size < minimumTilesRepository.minNumberOfTiles) {
+ // We ended up with not enough tiles (some may be not installed).
+ // Prepend the default set of tiles
+ launch { tileSpecRepository.prependDefault(currentUser.value) }
+ } else if (resolvedSpecs != newTileList) {
// There were some tiles that couldn't be created. Change the value in
// the
// repository
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
index 7d2c6c8f70fb..e237ca9f462b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
@@ -221,6 +221,15 @@ constructor(
)
}
+ fun logSettingsRestoredOnUserSetupComplete(userId: Int) {
+ restoreLogBuffer.log(
+ RESTORE_TAG,
+ LogLevel.DEBUG,
+ { int1 = userId },
+ { "Restored from single intent after user setup complete for user $int1" }
+ )
+ }
+
fun logSettingsRestored(restoreData: RestoreData) {
restoreLogBuffer.log(
RESTORE_TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 494c86c7b8c8..0add4443fa7a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -19,7 +19,6 @@ package com.android.systemui.scene.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
-import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.ObservableTransitionState
@@ -50,7 +49,6 @@ class SceneInteractor
constructor(
@Application private val applicationScope: CoroutineScope,
private val repository: SceneContainerRepository,
- private val powerInteractor: PowerInteractor,
private val logger: SceneLogger,
private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
) {
@@ -189,9 +187,4 @@ constructor(
fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
repository.setTransitionState(transitionState)
}
-
- /** Handles a user input event. */
- fun onUserInput() {
- powerInteractor.onUserTouch()
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 605a5d9b6772..b642d38289fe 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -41,6 +41,7 @@ import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
@@ -87,6 +88,7 @@ constructor(
private val windowController: NotificationShadeWindowController,
private val deviceProvisioningInteractor: DeviceProvisioningInteractor,
private val centralSurfaces: CentralSurfaces,
+ private val headsUpInteractor: HeadsUpNotificationInteractor,
) : CoreStartable {
override fun start() {
@@ -147,6 +149,15 @@ constructor(
}
}
}
+ .combine(headsUpInteractor.isHeadsUpOrAnimatingAway) {
+ visibilityForTransitionState,
+ isHeadsUpOrAnimatingAway ->
+ if (isHeadsUpOrAnimatingAway) {
+ true to "showing a HUN"
+ } else {
+ visibilityForTransitionState
+ }
+ }
.distinctUntilChanged()
} else {
flowOf(false to "Device not provisioned or Factory Reset Protection active")
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
index 7cff7ff1fd71..4c2c97981702 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
@@ -25,8 +25,8 @@ import android.view.View
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.core.view.updateMargins
-import com.android.systemui.res.R
import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.res.R
/** A view that can serve as the root of the main SysUI window. */
open class WindowRootView(
@@ -71,6 +71,7 @@ open class WindowRootView(
override fun onApplyWindowInsets(windowInsets: WindowInsets): WindowInsets? {
val insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars())
+ val displayCutout = rootWindowInsets.displayCutout
if (fitsSystemWindows) {
val paddingChanged = insets.top != paddingTop || insets.bottom != paddingBottom
@@ -78,22 +79,23 @@ open class WindowRootView(
if (paddingChanged) {
setPadding(0, 0, 0, 0)
}
+
+ val pairInsets: Pair<Int, Int> =
+ layoutInsetsController.getinsets(windowInsets, displayCutout)
+ leftInset = pairInsets.first
+ rightInset = pairInsets.second
+ applyMargins()
} else {
val changed =
paddingLeft != 0 || paddingRight != 0 || paddingTop != 0 || paddingBottom != 0
if (changed) {
setPadding(0, 0, 0, 0)
}
+
+ leftInset = 0
+ rightInset = 0
}
- leftInset = 0
- rightInset = 0
- val displayCutout = rootWindowInsets.displayCutout
- val pairInsets: Pair<Int, Int> =
- layoutInsetsController.getinsets(windowInsets, displayCutout)
- leftInset = pairInsets.first
- rightInset = pairInsets.second
- applyMargins()
return windowInsets
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index 5d290cefcd55..4cd3baa7a52e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -17,8 +17,10 @@
package com.android.systemui.scene.ui.viewmodel
import android.view.MotionEvent
+import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
@@ -33,6 +35,7 @@ class SceneContainerViewModel
constructor(
private val sceneInteractor: SceneInteractor,
private val falsingInteractor: FalsingInteractor,
+ private val powerInteractor: PowerInteractor,
) {
/**
* Keys of all scenes in the container.
@@ -63,7 +66,7 @@ constructor(
* Call this before the [MotionEvent] starts to propagate through the UI hierarchy.
*/
fun onMotionEvent(event: MotionEvent) {
- sceneInteractor.onUserInput()
+ powerInteractor.onUserTouch()
falsingInteractor.onTouchEvent(event)
}
@@ -77,7 +80,33 @@ constructor(
falsingInteractor.onMotionEventComplete()
}
- companion object {
- private const val SCENE_TRANSITION_LOGGING_REASON = "user input"
+ /**
+ * Returns `true` if a change to [toScene] is currently allowed; `false` otherwise.
+ *
+ * This is invoked only for user-initiated transitions. The goal is to check with the falsing
+ * system whether the change from the current scene to the given scene should be rejected due to
+ * it being a false touch.
+ */
+ fun canChangeScene(toScene: SceneKey): Boolean {
+ val interactionTypeOrNull =
+ when (toScene) {
+ SceneKey.Bouncer -> Classifier.BOUNCER_UNLOCK
+ SceneKey.Gone -> Classifier.UNLOCK
+ SceneKey.Shade -> Classifier.NOTIFICATION_DRAG_DOWN
+ SceneKey.QuickSettings -> Classifier.QUICK_SETTINGS
+ else -> null
+ }
+
+ return interactionTypeOrNull?.let { interactionType ->
+ // It's important that the falsing system is always queried, even if no enforcement will
+ // occur. This helps build up the right signal in the system.
+ val isFalseTouch = falsingInteractor.isFalseTouch(interactionType)
+
+ // Only enforce falsing if moving from the lockscreen scene to a new scene.
+ val fromLockscreenScene = currentScene.value == SceneKey.Lockscreen
+
+ !fromLockscreenScene || !isFalseTouch
+ }
+ ?: true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
index 238a552604ca..2c0bddecc58e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
@@ -58,8 +58,16 @@ constructor(
}
override fun playCameraSound(): Deferred<Unit> {
- return coroutineScope.async("playCameraSound", bgDispatcher) { player.await()?.start() }
+ return coroutineScope.async("playCameraSound", bgDispatcher) {
+ try {
+ player.await()?.start()
+ } catch (e: IllegalStateException) {
+ Log.w(TAG, "Screenshot sound failed to play", e)
+ releaseScreenshotSound()
+ }
+ }
}
+
override fun releaseScreenshotSound(): Deferred<Unit> {
return coroutineScope.async("releaseScreenshotSound", bgDispatcher) {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index d6f1ed9c3334..1a997a764055 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -50,6 +50,9 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider {
* List of profiles associated with the current user.
*
* Quiet work profiles will still appear here, but will have the `QUIET_MODE` flag.
+ *
+ * Disabled work profiles will also appear here. Listeners will be notified when profiles go
+ * from disabled to enabled (as UserInfo are immutable) with the updated list.
*/
val userProfiles: List<UserInfo>
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 95d9bc490caa..7068f5fcc32b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -149,9 +149,9 @@ import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransition
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
@@ -165,6 +165,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController.StateList
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.power.shared.model.WakefulnessModel;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.data.repository.FlingInfo;
import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
@@ -4595,8 +4596,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
public void onViewAttachedToWindow(View v) {
mFragmentService.getFragmentHostManager(mView)
.addTagListener(QS.TAG, mQsController.getQsFragmentListener());
- mStatusBarStateController.addCallback(mStatusBarStateListener);
- mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState());
+ if (!SceneContainerFlag.isEnabled()) {
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+ mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState());
+ }
mConfigurationController.addCallback(mConfigurationListener);
// Theme might have changed between inflating this view and attaching it to the
// window, so
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index f7fed537a167..8f9cef37dac7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -60,6 +60,7 @@ 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.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.scene.ui.view.WindowRootViewComponent;
import com.android.systemui.settings.UserTracker;
@@ -506,7 +507,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
private void applyFitsSystemWindows(NotificationShadeWindowState state) {
boolean fitsSystemWindows = !state.isKeyguardShowingAndNotOccluded();
- if (mWindowRootView != null
+ if (!SceneContainerFlag.isEnabled() && mWindowRootView != null
&& mWindowRootView.getFitsSystemWindows() != fitsSystemWindows) {
mWindowRootView.setFitsSystemWindows(fitsSystemWindows);
mWindowRootView.requestApplyInsets();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index f7b9e4e35ef8..e7f970050033 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -71,8 +71,8 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.res.R;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
index d393f0d0b72b..4054a86960d3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
@@ -17,6 +17,8 @@
package com.android.systemui.shade
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shade.data.repository.PrivacyChipRepository
+import com.android.systemui.shade.data.repository.PrivacyChipRepositoryImpl
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.data.repository.ShadeRepositoryImpl
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor
@@ -61,4 +63,8 @@ abstract class ShadeEmptyImplModule {
abstract fun bindsShadeAnimationInteractor(
sai: ShadeAnimationInteractorEmptyImpl
): ShadeAnimationInteractor
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindsPrivacyChipRepository(impl: PrivacyChipRepositoryImpl): PrivacyChipRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 86fdceea57ef..5632766f2633 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -18,6 +18,8 @@ package com.android.systemui.shade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.shade.data.repository.PrivacyChipRepository
+import com.android.systemui.shade.data.repository.PrivacyChipRepositoryImpl
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.data.repository.ShadeRepositoryImpl
import com.android.systemui.shade.domain.interactor.BaseShadeInteractor
@@ -124,4 +126,8 @@ abstract class ShadeModule {
abstract fun bindsShadeViewController(
notificationPanelViewController: NotificationPanelViewController
): ShadeViewController
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindsPrivacyChipRepository(impl: PrivacyChipRepositoryImpl): PrivacyChipRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt
new file mode 100644
index 000000000000..91c92cc8cd2f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.data.repository
+
+import android.content.IntentFilter
+import android.os.UserHandle
+import android.safetycenter.SafetyCenterManager
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.privacy.PrivacyConfig
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.privacy.PrivacyItemController
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
+
+interface PrivacyChipRepository {
+ /** Whether or not the Safety Center is enabled. */
+ val isSafetyCenterEnabled: StateFlow<Boolean>
+
+ /** The list of PrivacyItems to be displayed by the privacy chip. */
+ val privacyItems: StateFlow<List<PrivacyItem>>
+
+ /** Whether or not mic & camera indicators are enabled in the device privacy config. */
+ val isMicCameraIndicationEnabled: StateFlow<Boolean>
+
+ /** Whether or not location indicators are enabled in the device privacy config. */
+ val isLocationIndicationEnabled: StateFlow<Boolean>
+}
+
+@SysUISingleton
+class PrivacyChipRepositoryImpl
+@Inject
+constructor(
+ @Application applicationScope: CoroutineScope,
+ private val privacyConfig: PrivacyConfig,
+ private val privacyItemController: PrivacyItemController,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ broadcastDispatcher: BroadcastDispatcher,
+ private val safetyCenterManager: SafetyCenterManager,
+) : PrivacyChipRepository {
+ override val isSafetyCenterEnabled: StateFlow<Boolean> =
+ broadcastDispatcher
+ .broadcastFlow(
+ filter =
+ IntentFilter().apply {
+ addAction(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED)
+ },
+ user = UserHandle.SYSTEM,
+ map = { _, _ -> safetyCenterManager.isSafetyCenterEnabled }
+ )
+ .onStart { emit(safetyCenterManager.isSafetyCenterEnabled) }
+ .flowOn(backgroundDispatcher)
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ override val privacyItems: StateFlow<List<PrivacyItem>> =
+ conflatedCallbackFlow {
+ val callback =
+ object : PrivacyItemController.Callback {
+ override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
+ trySend(privacyItems)
+ }
+ }
+ privacyItemController.addCallback(callback)
+ awaitClose { privacyItemController.removeCallback(callback) }
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = emptyList(),
+ )
+
+ override val isMicCameraIndicationEnabled: StateFlow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : PrivacyConfig.Callback {
+ override fun onFlagMicCameraChanged(flag: Boolean) {
+ trySend(flag)
+ }
+ }
+ privacyConfig.addCallback(callback)
+ awaitClose { privacyConfig.removeCallback(callback) }
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = privacyItemController.micCameraAvailable,
+ )
+
+ override val isLocationIndicationEnabled: StateFlow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : PrivacyConfig.Callback {
+ override fun onFlagLocationChanged(flag: Boolean) {
+ trySend(flag)
+ }
+ }
+ privacyConfig.addCallback(callback)
+ awaitClose { privacyConfig.removeCallback(callback) }
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = privacyItemController.locationAvailable,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepository.kt
new file mode 100644
index 000000000000..a9bf2dfdef9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepository.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.data.repository
+
+import android.app.PendingIntent
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.NextAlarmController
+import javax.inject.Inject
+
+@SysUISingleton
+class ShadeHeaderClockRepository
+@Inject
+constructor(
+ nextAlarmController: NextAlarmController,
+) {
+ private val nextAlarmCallback =
+ NextAlarmController.NextAlarmChangeCallback { nextAlarm ->
+ nextAlarmIntent = nextAlarm?.showIntent
+ }
+
+ init {
+ nextAlarmController.addCallback(nextAlarmCallback)
+ }
+
+ /** Intent to show the next active alarm. */
+ var nextAlarmIntent: PendingIntent? = null
+ private set
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractor.kt
new file mode 100644
index 000000000000..4c6c31809275
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractor.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyDialogController
+import com.android.systemui.privacy.PrivacyDialogControllerV2
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.shade.data.repository.PrivacyChipRepository
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class PrivacyChipInteractor
+@Inject
+constructor(
+ @Application applicationScope: CoroutineScope,
+ private val repository: PrivacyChipRepository,
+ private val privacyDialogController: PrivacyDialogController,
+ private val privacyDialogControllerV2: PrivacyDialogControllerV2,
+ private val deviceProvisionedController: DeviceProvisionedController,
+) {
+ /** The list of PrivacyItems to be displayed by the privacy chip. */
+ val privacyItems: StateFlow<List<PrivacyItem>> = repository.privacyItems
+
+ /** Whether or not mic & camera indicators are enabled in the device privacy config. */
+ val isMicCameraIndicationEnabled: StateFlow<Boolean> = repository.isMicCameraIndicationEnabled
+
+ /** Whether or not location indicators are enabled in the device privacy config. */
+ val isLocationIndicationEnabled: StateFlow<Boolean> = repository.isLocationIndicationEnabled
+
+ /** Whether or not the privacy chip should be visible. */
+ val isChipVisible: StateFlow<Boolean> =
+ privacyItems
+ .map { it.isNotEmpty() }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ /** Whether or not the privacy chip is enabled in the device privacy config. */
+ val isChipEnabled: StateFlow<Boolean> =
+ combine(
+ isMicCameraIndicationEnabled,
+ isLocationIndicationEnabled,
+ ) { micCamera, location ->
+ micCamera || location
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ /** Notifies that the privacy chip was clicked. */
+ fun onPrivacyChipClicked(privacyChip: OngoingPrivacyChip) {
+ if (!deviceProvisionedController.isDeviceProvisioned) return
+
+ if (repository.isSafetyCenterEnabled.value) {
+ privacyDialogControllerV2.showDialog(privacyChip.context, privacyChip)
+ } else {
+ privacyDialogController.showDialog(privacyChip.context)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractor.kt
new file mode 100644
index 000000000000..186bfcbbc8e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractor.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.domain.interactor
+
+import android.content.Intent
+import android.provider.AlarmClock
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.shade.data.repository.ShadeHeaderClockRepository
+import javax.inject.Inject
+
+@SysUISingleton
+class ShadeHeaderClockInteractor
+@Inject
+constructor(
+ private val repository: ShadeHeaderClockRepository,
+ private val activityStarter: ActivityStarter,
+) {
+ /** Launch the clock activity. */
+ fun launchClockActivity() {
+ val nextAlarmIntent = repository.nextAlarmIntent
+ if (nextAlarmIntent != null) {
+ activityStarter.postStartActivityDismissingKeyguard(nextAlarmIntent)
+ } else {
+ activityStarter.postStartActivityDismissingKeyguard(
+ Intent(AlarmClock.ACTION_SHOW_ALARMS),
+ 0
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index 314637e4b27e..1191c0f247f9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -25,8 +25,11 @@ import android.os.UserHandle
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.res.R
-import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.shade.domain.interactor.PrivacyChipInteractor
+import com.android.systemui.shade.domain.interactor.ShadeHeaderClockInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
import java.util.Date
@@ -50,9 +53,10 @@ class ShadeHeaderViewModel
constructor(
@Application private val applicationScope: CoroutineScope,
context: Context,
- sceneInteractor: SceneInteractor,
mobileIconsInteractor: MobileIconsInteractor,
val mobileIconsViewModel: MobileIconsViewModel,
+ private val privacyChipInteractor: PrivacyChipInteractor,
+ private val clockInteractor: ShadeHeaderClockInteractor,
broadcastDispatcher: BroadcastDispatcher,
) {
/** True if there is exactly one mobile connection. */
@@ -64,6 +68,23 @@ constructor(
.map { list -> list.map { it.subscriptionId } }
.stateIn(applicationScope, SharingStarted.WhileSubscribed(), emptyList())
+ /** The list of PrivacyItems to be displayed by the privacy chip. */
+ val privacyItems: StateFlow<List<PrivacyItem>> = privacyChipInteractor.privacyItems
+
+ /** Whether or not mic & camera indicators are enabled in the device privacy config. */
+ val isMicCameraIndicationEnabled: StateFlow<Boolean> =
+ privacyChipInteractor.isMicCameraIndicationEnabled
+
+ /** Whether or not location indicators are enabled in the device privacy config. */
+ val isLocationIndicationEnabled: StateFlow<Boolean> =
+ privacyChipInteractor.isLocationIndicationEnabled
+
+ /** Whether or not the privacy chip should be visible. */
+ val isPrivacyChipVisible: StateFlow<Boolean> = privacyChipInteractor.isChipVisible
+
+ /** Whether or not the privacy chip is enabled in the device privacy config. */
+ val isPrivacyChipEnabled: StateFlow<Boolean> = privacyChipInteractor.isChipEnabled
+
private val longerPattern = context.getString(R.string.abbrev_wday_month_day_no_year_alarm)
private val shorterPattern = context.getString(R.string.abbrev_month_day_no_year)
private val longerDateFormat = MutableStateFlow(getFormatFromPattern(longerPattern))
@@ -97,6 +118,16 @@ constructor(
applicationScope.launch { updateDateTexts(false) }
}
+ /** Notifies that the privacy chip was clicked. */
+ fun onPrivacyChipClicked(privacyChip: OngoingPrivacyChip) {
+ privacyChipInteractor.onPrivacyChipClicked(privacyChip)
+ }
+
+ /** Notifies that the clock was clicked. */
+ fun onClockClicked() {
+ clockInteractor.launchClockActivity()
+ }
+
private fun updateDateTexts(invalidateFormats: Boolean) {
if (invalidateFormats) {
longerDateFormat.value = getFormatFromPattern(longerPattern)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index 9af2d58910b6..38358a8f244e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -19,7 +19,7 @@ package com.android.systemui.shade.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
@@ -28,6 +28,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/** Models UI state and handles user input for the shade scene. */
@@ -63,6 +64,16 @@ constructor(
),
)
+ /** Whether or not the shade container should be clickable. */
+ val isClickable: StateFlow<Boolean> =
+ upDestinationSceneKey
+ .map { it == SceneKey.Lockscreen }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false
+ )
+
/** Notifies that some content in the shade was clicked. */
fun onContentClicked() = deviceEntryInteractor.attemptDeviceEntry()
diff --git a/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt b/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt
new file mode 100644
index 000000000000..384acc493c4a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.slice
+
+import android.net.Uri
+import androidx.slice.Slice
+import androidx.slice.SliceViewManager
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+/**
+ * Returns updating [Slice] for a [sliceUri]. It's null when there is no slice available for the
+ * provided Uri. This can change overtime because of external changes (like device being
+ * connected/disconnected).
+ */
+fun SliceViewManager.sliceForUri(sliceUri: Uri): Flow<Slice?> =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val callback = SliceViewManager.SliceCallback { launch { send(it) } }
+
+ val slice = bindSlice(sliceUri)
+ send(slice)
+ registerSliceCallback(sliceUri, callback)
+ awaitClose { unregisterSliceCallback(sliceUri, callback) }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
index 0b470c179dbf..9f098e79f759 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
@@ -4,7 +4,7 @@ import android.content.Context
import android.util.IndentingPrintWriter
import android.util.MathUtils
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeLockscreenInteractor
import com.android.systemui.statusbar.policy.ConfigurationController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 4187a5449a9f..4ee83497b368 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -24,7 +24,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
@@ -69,7 +69,7 @@ constructor(
private val mediaHierarchyManager: MediaHierarchyManager,
private val scrimTransitionController: LockscreenShadeScrimTransitionController,
private val keyguardTransitionControllerFactory:
- LockscreenShadeKeyguardTransitionController.Factory,
+ LockscreenShadeKeyguardTransitionController.Factory,
private val depthController: NotificationShadeDepthController,
private val context: Context,
private val splitShadeOverScrollerFactory: SplitShadeLockScreenOverScroller.Factory,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt
index 692a9977c364..4ab78aab372d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt
@@ -1,8 +1,8 @@
package com.android.systemui.statusbar
+import android.app.Flags.lifetimeExtensionRefactor
import android.app.Notification
import android.os.RemoteException
-import android.util.Log
import com.android.internal.statusbar.IStatusBarService
import com.android.internal.statusbar.NotificationVisibility
import com.android.systemui.dagger.SysUISingleton
@@ -58,9 +58,14 @@ public class NotificationClickNotifier @Inject constructor(
} catch (e: RemoteException) {
// nothing
}
+ if (lifetimeExtensionRefactor()) {
+ notifyListenersAboutInteraction(key)
+ }
}
- mainExecutor.execute {
- notifyListenersAboutInteraction(key)
+ if (!lifetimeExtensionRefactor()) {
+ mainExecutor.execute {
+ notifyListenersAboutInteraction(key)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 9c4625e91110..d465973a5d12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -30,9 +30,9 @@ import android.util.Log;
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.shared.model.MediaData;
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData;
import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 537f8a866fed..36fc9bb3a2da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_TO_AOD;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -43,19 +44,26 @@ import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardClockSwitch;
import com.android.systemui.DejankUtils;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.res.R;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.scene.shared.model.SceneKey;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.util.Compile;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.google.common.base.Preconditions;
+
import dagger.Lazy;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.Map;
import javax.inject.Inject;
@@ -97,6 +105,8 @@ public class StatusBarStateControllerImpl implements
private final InteractionJankMonitor mInteractionJankMonitor;
private final JavaAdapter mJavaAdapter;
private final Lazy<ShadeInteractor> mShadeInteractorLazy;
+ private final Lazy<DeviceUnlockedInteractor> mDeviceUnlockedInteractorLazy;
+ private final Lazy<SceneInteractor> mSceneInteractorLazy;
private int mState;
private int mLastState;
private int mUpcomingState;
@@ -160,11 +170,15 @@ public class StatusBarStateControllerImpl implements
UiEventLogger uiEventLogger,
InteractionJankMonitor interactionJankMonitor,
JavaAdapter javaAdapter,
- Lazy<ShadeInteractor> shadeInteractorLazy) {
+ Lazy<ShadeInteractor> shadeInteractorLazy,
+ Lazy<DeviceUnlockedInteractor> deviceUnlockedInteractorLazy,
+ Lazy<SceneInteractor> sceneInteractorLazy) {
mUiEventLogger = uiEventLogger;
mInteractionJankMonitor = interactionJankMonitor;
mJavaAdapter = javaAdapter;
mShadeInteractorLazy = shadeInteractorLazy;
+ mDeviceUnlockedInteractorLazy = deviceUnlockedInteractorLazy;
+ mSceneInteractorLazy = sceneInteractorLazy;
for (int i = 0; i < HISTORY_SIZE; i++) {
mHistoricalRecords[i] = new HistoricalState();
}
@@ -174,6 +188,15 @@ public class StatusBarStateControllerImpl implements
public void start() {
mJavaAdapter.alwaysCollectFlow(mShadeInteractorLazy.get().isAnyExpanded(),
this::onShadeOrQsExpanded);
+
+ if (SceneContainerFlag.isEnabled()) {
+ mJavaAdapter.alwaysCollectFlow(
+ combineFlows(
+ mDeviceUnlockedInteractorLazy.get().isDeviceUnlocked(),
+ mSceneInteractorLazy.get().getCurrentScene(),
+ this::calculateStateFromSceneFramework),
+ this::onStatusBarStateChanged);
+ }
}
@Override
@@ -183,6 +206,10 @@ public class StatusBarStateControllerImpl implements
@Override
public boolean setState(int state, boolean force) {
+ if (SceneContainerFlag.isEnabled()) {
+ return false;
+ }
+
if (state > MAX_STATE || state < MIN_STATE) {
throw new IllegalArgumentException("Invalid state " + state);
}
@@ -194,6 +221,14 @@ public class StatusBarStateControllerImpl implements
return false;
}
+ updateStateAndNotifyListeners(state);
+ return true;
+ }
+
+ /**
+ * Updates the {@link StatusBarState} and notifies registered listeners, if needed.
+ */
+ private void updateStateAndNotifyListeners(int state) {
if (state != mUpcomingState) {
Log.d(TAG, "setState: requested state " + StatusBarState.toString(state)
+ "!= upcomingState: " + StatusBarState.toString(mUpcomingState) + ". "
@@ -229,15 +264,16 @@ public class StatusBarStateControllerImpl implements
}
DejankUtils.stopDetectingBlockingIpcs(tag);
}
-
- return true;
}
@Override
public void setUpcomingState(int nextState) {
+ if (SceneContainerFlag.isEnabled()) {
+ return;
+ }
+
recordHistoricalState(nextState /* newState */, mState /* lastState */, true);
updateUpcomingState(nextState);
-
}
private void updateUpcomingState(int upcomingState) {
@@ -468,7 +504,7 @@ public class StatusBarStateControllerImpl implements
@Override
public boolean goingToFullShade() {
- return mState == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide;
+ return getState() == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide;
}
@Override
@@ -599,6 +635,37 @@ public class StatusBarStateControllerImpl implements
state.mUpcoming = upcoming;
}
+ private int calculateStateFromSceneFramework(
+ boolean isDeviceUnlocked,
+ SceneKey currentScene) {
+ SceneContainerFlag.isUnexpectedlyInLegacyMode();
+
+ if (isDeviceUnlocked) {
+ return StatusBarState.SHADE;
+ } else {
+ return Preconditions.checkNotNull(sStatusBarStateByLockedSceneKey.get(currentScene));
+ }
+ }
+
+ /** Notifies that the {@link StatusBarState} has changed to the given new state. */
+ private void onStatusBarStateChanged(int newState) {
+ SceneContainerFlag.isUnexpectedlyInLegacyMode();
+
+ if (newState == mState) {
+ return;
+ }
+
+ updateStateAndNotifyListeners(newState);
+ }
+
+ private static final Map<SceneKey, Integer> sStatusBarStateByLockedSceneKey = Map.of(
+ SceneKey.Lockscreen.INSTANCE, StatusBarState.KEYGUARD,
+ SceneKey.Bouncer.INSTANCE, StatusBarState.KEYGUARD,
+ SceneKey.Communal.INSTANCE, StatusBarState.KEYGUARD,
+ SceneKey.Shade.INSTANCE, StatusBarState.SHADE_LOCKED,
+ SceneKey.QuickSettings.INSTANCE, StatusBarState.SHADE_LOCKED
+ );
+
/**
* For keeping track of our previous state to help with debugging
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index f6d99bdefb9f..f960fcafafc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -33,7 +33,7 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpHandler;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shade.NotificationPanelViewController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
index 16af9d9c82ae..072f56d2429d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
-import static com.android.systemui.media.controls.pipeline.MediaDataManagerKt.isMediaNotification;
+import static com.android.systemui.media.controls.domain.pipeline.MediaDataManagerKt.isMediaNotification;
import android.os.RemoteException;
import android.service.notification.StatusBarNotification;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 2a1ec3e9c64f..6548967c7462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -16,12 +16,18 @@
package com.android.systemui.statusbar.notification.dagger;
+import android.app.NotificationManager;
import android.content.Context;
import android.service.notification.NotificationListenerService;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.settingslib.statusbar.notification.data.repository.NotificationsSoundPolicyRepository;
+import com.android.settingslib.statusbar.notification.data.repository.NotificationsSoundPolicyRepositoryImpl;
+import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -79,13 +85,15 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import javax.inject.Provider;
+
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
-
-import javax.inject.Provider;
+import kotlin.coroutines.CoroutineContext;
+import kotlinx.coroutines.CoroutineScope;
/**
* Dagger Module for classes found within the com.android.systemui.statusbar.notification package.
@@ -259,4 +267,22 @@ public interface NotificationsModule {
@ClassKey(VisualInterruptionDecisionProvider.class)
CoreStartable startVisualInterruptionDecisionProvider(
VisualInterruptionDecisionProvider provider);
+
+ @Provides
+ @SysUISingleton
+ public static NotificationsSoundPolicyRepository provideNotificationsSoundPolicyRepository(
+ Context context,
+ NotificationManager notificationManager,
+ @Application CoroutineScope coroutineScope,
+ @Background CoroutineContext coroutineContext) {
+ return new NotificationsSoundPolicyRepositoryImpl(context, notificationManager,
+ coroutineScope, coroutineContext);
+ }
+
+ @Provides
+ @SysUISingleton
+ public static NotificationsSoundPolicyInteractor provideNotificationsSoundPolicyInteractror(
+ NotificationsSoundPolicyRepository repository) {
+ return new NotificationsSoundPolicyInteractor(repository);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
index b187cf15cccd..e5e5292d9a94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
@@ -15,6 +15,9 @@
*/
package com.android.systemui.statusbar.notification.data
+import com.android.systemui.statusbar.notification.data.repository.HeadsUpNotificationRepository
+import com.android.systemui.statusbar.notification.data.repository.HeadsUpNotificationRepositoryImpl
+import dagger.Binds
import dagger.Module
@Module(
@@ -23,4 +26,9 @@ import dagger.Module
NotificationSettingsRepositoryModule::class,
]
)
-interface NotificationDataLayerModule
+interface NotificationDataLayerModule {
+ @Binds
+ fun bindHeadsUpNotificationRepository(
+ impl: HeadsUpNotificationRepositoryImpl
+ ): HeadsUpNotificationRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationRepository.kt
new file mode 100644
index 000000000000..d60ee9896758
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationRepository.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.data.repository
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+class HeadsUpNotificationRepositoryImpl
+@Inject
+constructor(
+ headsUpManager: HeadsUpManager,
+) : HeadsUpNotificationRepository {
+ override val hasPinnedHeadsUp: Flow<Boolean> = conflatedCallbackFlow {
+ val listener =
+ object : OnHeadsUpChangedListener {
+ override fun onHeadsUpPinnedModeChanged(inPinnedMode: Boolean) {
+ trySend(headsUpManager.hasPinnedHeadsUp())
+ }
+
+ override fun onHeadsUpPinned(entry: NotificationEntry?) {
+ trySend(headsUpManager.hasPinnedHeadsUp())
+ }
+
+ override fun onHeadsUpUnPinned(entry: NotificationEntry?) {
+ trySend(headsUpManager.hasPinnedHeadsUp())
+ }
+
+ override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
+ trySend(headsUpManager.hasPinnedHeadsUp())
+ }
+ }
+ trySend(headsUpManager.hasPinnedHeadsUp())
+ headsUpManager.addListener(listener)
+ awaitClose { headsUpManager.removeListener(listener) }
+ }
+}
+
+interface HeadsUpNotificationRepository {
+ val hasPinnedHeadsUp: Flow<Boolean>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
new file mode 100644
index 000000000000..5c8f354de485
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.domain.interactor
+
+import com.android.systemui.statusbar.notification.data.repository.HeadsUpNotificationRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+class HeadsUpNotificationInteractor @Inject constructor(repository: HeadsUpNotificationRepository) {
+ val isHeadsUpOrAnimatingAway: Flow<Boolean> =
+ // TODO(b/296118689): Needs to include the animating away state.
+ repository.hasPinnedHeadsUp
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt
deleted file mode 100644
index 4deebdb8de7d..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.notification.row
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
-import com.android.internal.widget.CallLayout
-import javax.inject.Inject
-
-class CallLayoutSetDataAsyncFactory @Inject constructor() : NotifRemoteViewsFactory {
- override fun instantiate(
- row: ExpandableNotificationRow,
- @NotificationRowContentBinder.InflationFlag layoutType: Int,
- parent: View?,
- name: String,
- context: Context,
- attrs: AttributeSet
- ): View? =
- if (name == CallLayout::class.java.name)
- CallLayout(context, attrs).apply { setSetDataAsyncEnabled(true) }
- else null
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
index 195fe785b538..dab89c5235b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
@@ -31,7 +31,6 @@ constructor(
featureFlags: FeatureFlags,
precomputedTextViewFactory: PrecomputedTextViewFactory,
bigPictureLayoutInflaterFactory: BigPictureLayoutInflaterFactory,
- callLayoutSetDataAsyncFactory: CallLayoutSetDataAsyncFactory,
optimizedLinearLayoutFactory: NotificationOptimizedLinearLayoutFactory
) : NotifRemoteViewsFactoryContainer {
override val factories: Set<NotifRemoteViewsFactory> = buildSet {
@@ -39,9 +38,6 @@ constructor(
if (featureFlags.isEnabled(Flags.BIGPICTURE_NOTIFICATION_LAZY_LOADING)) {
add(bigPictureLayoutInflaterFactory)
}
- if (featureFlags.isEnabled(Flags.CALL_LAYOUT_ASYNC_SET_DATA)) {
- add(callLayoutSetDataAsyncFactory)
- }
if (notifLinearlayoutOptimized()) {
add(optimizedLinearLayoutFactory)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 913d5f6d3848..e288e857bf4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -43,7 +43,7 @@ import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ImageMessageConsumer;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.NotifInflation;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.InflationTask;
@@ -82,7 +82,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final NotificationRemoteInputManager mRemoteInputManager;
private final NotifRemoteViewCache mRemoteViewCache;
private final ConversationNotificationProcessor mConversationProcessor;
- private final Executor mBgExecutor;
+ private final Executor mInflationExecutor;
private final SmartReplyStateInflater mSmartReplyStateInflater;
private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
private final NotificationContentInflaterLogger mLogger;
@@ -93,7 +93,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
NotificationRemoteInputManager remoteInputManager,
ConversationNotificationProcessor conversationProcessor,
MediaFeatureFlag mediaFeatureFlag,
- @Background Executor bgExecutor,
+ @NotifInflation Executor inflationExecutor,
SmartReplyStateInflater smartRepliesInflater,
NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider,
NotificationContentInflaterLogger logger) {
@@ -101,7 +101,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mRemoteInputManager = remoteInputManager;
mConversationProcessor = conversationProcessor;
mIsMediaInQS = mediaFeatureFlag.getEnabled();
- mBgExecutor = bgExecutor;
+ mInflationExecutor = inflationExecutor;
mSmartReplyStateInflater = smartRepliesInflater;
mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider;
mLogger = logger;
@@ -138,7 +138,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
cancelContentViewFrees(row, contentToBind);
AsyncInflationTask task = new AsyncInflationTask(
- mBgExecutor,
+ mInflationExecutor,
mInflateSynchronously,
/* reInflateFlags = */ contentToBind,
mRemoteViewCache,
@@ -157,7 +157,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground());
} else {
- task.executeOnExecutor(mBgExecutor);
+ task.executeOnExecutor(mInflationExecutor);
}
}
@@ -208,7 +208,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
apply(
- mBgExecutor,
+ mInflationExecutor,
inflateSynchronously,
result,
reInflateFlags,
@@ -416,7 +416,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
private static CancellationSignal apply(
- Executor bgExecutor,
+ Executor inflationExecutor,
boolean inflateSynchronously,
InflationProgress result,
@InflationFlag int reInflateFlags,
@@ -447,7 +447,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
};
logger.logAsyncTaskProgress(entry, "applying contracted view");
- applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
+ applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags, flag,
remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback,
privateLayout, privateLayout.getContractedChild(),
privateLayout.getVisibleWrapper(
@@ -474,11 +474,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
};
logger.logAsyncTaskProgress(entry, "applying expanded view");
- applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
- remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
+ applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags,
+ flag, remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
callback, privateLayout, privateLayout.getExpandedChild(),
- privateLayout.getVisibleWrapper(
- NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
+ privateLayout.getVisibleWrapper(VISIBLE_TYPE_EXPANDED), runningInflations,
applyCallback, logger);
}
}
@@ -502,11 +501,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
};
logger.logAsyncTaskProgress(entry, "applying heads up view");
- applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
- remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
+ applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags,
+ flag, remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
callback, privateLayout, privateLayout.getHeadsUpChild(),
- privateLayout.getVisibleWrapper(
- VISIBLE_TYPE_HEADSUP), runningInflations,
+ privateLayout.getVisibleWrapper(VISIBLE_TYPE_HEADSUP), runningInflations,
applyCallback, logger);
}
}
@@ -529,7 +527,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
};
logger.logAsyncTaskProgress(entry, "applying public view");
- applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
+ applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags, flag,
remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback,
publicLayout, publicLayout.getContractedChild(),
publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
@@ -551,7 +549,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
@VisibleForTesting
static void applyRemoteView(
- Executor bgExecutor,
+ Executor inflationExecutor,
boolean inflateSynchronously,
final InflationProgress result,
final @InflationFlag int reInflateFlags,
@@ -655,14 +653,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder
cancellationSignal = newContentView.applyAsync(
result.packageContext,
parentLayout,
- bgExecutor,
+ inflationExecutor,
listener,
remoteViewClickHandler);
} else {
cancellationSignal = newContentView.reapplyAsync(
result.packageContext,
existingView,
- bgExecutor,
+ inflationExecutor,
listener,
remoteViewClickHandler);
}
@@ -918,7 +916,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final boolean mUsesIncreasedHeadsUpHeight;
private final @InflationFlag int mReInflateFlags;
private final NotifRemoteViewCache mRemoteViewCache;
- private final Executor mBgExecutor;
+ private final Executor mInflationExecutor;
private ExpandableNotificationRow mRow;
private Exception mError;
private RemoteViews.InteractionHandler mRemoteViewClickHandler;
@@ -930,7 +928,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final NotificationContentInflaterLogger mLogger;
private AsyncInflationTask(
- Executor bgExecutor,
+ Executor inflationExecutor,
boolean inflateSynchronously,
@InflationFlag int reInflateFlags,
NotifRemoteViewCache cache,
@@ -948,7 +946,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
NotificationContentInflaterLogger logger) {
mEntry = entry;
mRow = row;
- mBgExecutor = bgExecutor;
+ mInflationExecutor = inflationExecutor;
mInflateSynchronously = inflateSynchronously;
mReInflateFlags = reInflateFlags;
mRemoteViewCache = cache;
@@ -1067,7 +1065,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if (mError == null) {
// Logged in detail in apply.
mCancellationSignal = apply(
- mBgExecutor,
+ mInflationExecutor,
mInflateSynchronously,
result,
mReInflateFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index cfc433a09c4d..d269eda6795a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -19,7 +19,7 @@ import android.annotation.ColorInt
import android.util.Log
import android.view.View
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.SourceType
import com.android.systemui.statusbar.notification.collection.render.MediaContainerController
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 7925a1ce97ee..e397a70ea1f2 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
@@ -573,7 +573,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* Do notifications dismiss with normal transitioning
*/
private boolean mDismissUsingRowTranslationX = true;
- private NotificationEntry mTopHeadsUpEntry;
+ private ExpandableNotificationRow mTopHeadsUpRow;
private long mNumHeadsUp;
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
private final ScreenOffAnimationController mScreenOffAnimationController;
@@ -1688,10 +1688,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* is mainly used when dragging down from a heads up notification.
*/
private int getTopHeadsUpPinnedHeight() {
- if (mTopHeadsUpEntry == null) {
+ if (mTopHeadsUpRow == null) {
return 0;
}
- ExpandableNotificationRow row = mTopHeadsUpEntry.getRow();
+ ExpandableNotificationRow row = mTopHeadsUpRow;
if (row.isChildInGroup()) {
final NotificationEntry groupSummary =
mGroupMembershipManager.getGroupSummary(row.getEntry());
@@ -1872,8 +1872,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (slidingChild instanceof ExpandableNotificationRow row) {
NotificationEntry entry = row.getEntry();
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
- && mTopHeadsUpEntry.getRow() != row
- && mGroupMembershipManager.getGroupSummary(mTopHeadsUpEntry) != entry) {
+ && mTopHeadsUpRow != row
+ && mGroupMembershipManager.getGroupSummary(mTopHeadsUpRow.getEntry())
+ != entry) {
continue;
}
return row.getViewAtPosition(touchY - childTop);
@@ -5724,8 +5725,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mShelf.updateAppearance();
}
- void setTopHeadsUpEntry(NotificationEntry topEntry) {
- mTopHeadsUpEntry = topEntry;
+ void setTopHeadsUpRow(ExpandableNotificationRow topHeadsUpRow) {
+ mTopHeadsUpRow = topHeadsUpRow;
}
void setNumHeadsUp(long numHeadsUp) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 715505d81d71..7c138776d5a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -74,7 +74,7 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -692,7 +692,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
long numEntries = mHeadsUpManager.getAllEntries().count();
NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
mView.setNumHeadsUp(numEntries);
- mView.setTopHeadsUpEntry(topEntry);
+ mView.setTopHeadsUpRow(topEntry != null ? topEntry.getRow() : null);
generateHeadsUpAnimation(entry, isHeadsUp);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 30708b708f25..2d9c63efee53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -23,7 +23,7 @@ import androidx.annotation.VisibleForTesting
import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.SysuiStatusBarStateController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
index a1577854e732..76495cb23947 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
@@ -20,12 +20,14 @@ import android.content.Context
import android.util.TypedValue
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel
import kotlin.math.roundToInt
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.launch
@@ -40,8 +42,9 @@ object NotificationStackAppearanceViewBinder {
viewModel: NotificationStackAppearanceViewModel,
ambientState: AmbientState,
controller: NotificationStackScrollLayoutController,
+ @Main mainImmediateDispatcher: CoroutineDispatcher,
): DisposableHandle {
- return view.repeatWhenAttached {
+ return view.repeatWhenAttached(mainImmediateDispatcher) {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.stackBounds.collect { bounds ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 77e146b98242..5191053b6f72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -91,10 +91,10 @@ object SharedNotificationContainerBinder {
if (!sceneContainerFlags.flexiNotifsEnabled()) {
launch {
// Only temporarily needed, until flexi notifs go live
- viewModel.shadeCollpaseFadeIn.collect { fadeIn ->
+ viewModel.shadeCollapseFadeIn.collect { fadeIn ->
if (fadeIn) {
android.animation.ValueAnimator.ofFloat(0f, 1f).apply {
- duration = 350
+ duration = 250
addUpdateListener { animation ->
controller.setMaxAlphaForExpansion(
animation.getAnimatedFraction()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 6b949a3461e7..052e35c44bbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -133,6 +133,21 @@ constructor(
.distinctUntilChanged()
.onStart { emit(false) }
+ /**
+ * Shade locked is a legacy concept, but necessary to mimic current functionality. Listen for
+ * both SHADE_LOCKED and shade/qs expansion in order to determine lock state, as one can arrive
+ * before the other.
+ */
+ private val isShadeLocked: Flow<Boolean> =
+ combine(
+ keyguardInteractor.statusBarState.map { it == SHADE_LOCKED },
+ shadeInteractor.qsExpansion.map { it > 0f },
+ shadeInteractor.shadeExpansion.map { it > 0f },
+ ) { isShadeLocked, isQsExpanded, isShadeExpanded ->
+ isShadeLocked && (isQsExpanded || isShadeExpanded)
+ }
+ .distinctUntilChanged()
+
val shadeCollapseFadeInComplete = MutableStateFlow(false)
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
@@ -190,7 +205,7 @@ constructor(
)
/** Are we purely on the glanceable hub without the shade/qs? */
- internal val isOnGlanceableHubWithoutShade: Flow<Boolean> =
+ val isOnGlanceableHubWithoutShade: Flow<Boolean> =
combine(
communalInteractor.isIdleOnCommunal,
// Shade with notifications
@@ -208,12 +223,12 @@ constructor(
)
/** Fade in only for use after the shade collapses */
- val shadeCollpaseFadeIn: Flow<Boolean> =
+ val shadeCollapseFadeIn: Flow<Boolean> =
flow {
while (currentCoroutineContext().isActive) {
emit(false)
// Wait for shade to be fully expanded
- keyguardInteractor.statusBarState.first { it == SHADE_LOCKED }
+ isShadeLocked.first { it }
// ... and then for it to be collapsed
isOnLockscreenWithoutShade.first { it }
emit(true)
@@ -330,16 +345,16 @@ constructor(
// shade expansion or swipe to dismiss
combineTransform(
isOnLockscreenWithoutShade,
- shadeCollpaseFadeIn,
+ shadeCollapseFadeIn,
alphaForShadeAndQsExpansion,
keyguardInteractor.dismissAlpha,
) {
isOnLockscreenWithoutShade,
- shadeCollpaseFadeIn,
+ shadeCollapseFadeIn,
alphaForShadeAndQsExpansion,
dismissAlpha ->
if (isOnLockscreenWithoutShade) {
- if (!shadeCollpaseFadeIn && dismissAlpha != null) {
+ if (!shadeCollapseFadeIn && dismissAlpha != null) {
emit(dismissAlpha)
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
index 4fe9c8ccca0b..f3a4f0e64924 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
@@ -24,6 +24,7 @@ import android.view.ViewGroup
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.annotation.GravityInt
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
@@ -56,6 +57,7 @@ class ComponentSystemUIDialog(
sysUiState: SysUiState,
broadcastDispatcher: BroadcastDispatcher,
dialogTransitionAnimator: DialogTransitionAnimator,
+ @GravityInt private val dialogGravity: Int?,
) :
SystemUIDialog(
context,
@@ -90,6 +92,7 @@ class ComponentSystemUIDialog(
@CallSuper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ dialogGravity?.let { window?.setGravity(it) }
onBackPressedDispatcher.setOnBackInvokedDispatcher(onBackInvokedDispatcher)
savedStateRegistryController.performRestore(savedInstanceState)
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index 323ab8065a32..613efaa148f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -185,7 +185,7 @@ constructor(
*/
fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Insets =
traceSection(tag = "StatusBarContentInsetsProvider.getStatusBarContentInsetsForRotation") {
- val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplay()
+ val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplayAndRotation()
val displayCutout = sysUICutout?.cutout
val key = getCacheKey(rotation, displayCutout)
@@ -227,7 +227,7 @@ constructor(
*/
@JvmOverloads
fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect {
- val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplay()
+ val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplayAndRotation()
val displayCutout = sysUICutout?.cutout
val key = getCacheKey(rotation, displayCutout)
return insetsCache[key]
@@ -528,7 +528,7 @@ private fun getStatusBarContentBounds(
var leftMargin = minLeft
var rightMargin = minRight
for (cutoutRect in cutoutRects) {
- val protectionRect = sysUICutout.cameraProtection?.cutoutBounds
+ val protectionRect = sysUICutout.cameraProtection?.bounds
val actualCutoutRect =
if (protectionRect?.intersects(cutoutRect) == true) {
rectUnion(cutoutRect, protectionRect)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt
index 553edf9b5d13..1edd4d11351c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone
import android.content.Context
+import androidx.annotation.GravityInt
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Application
@@ -43,11 +44,14 @@ constructor(
* @param context the [Context] in which the dialog will be constructed.
* @param dismissOnDeviceLock whether the dialog should be automatically dismissed when the
* device is locked (true by default).
+ * @param dialogGravity is one of the [android.view.Gravity] and determines dialog position on
+ * the screen.
*/
fun create(
context: Context = this.applicationContext,
theme: Int = SystemUIDialog.DEFAULT_THEME,
dismissOnDeviceLock: Boolean = SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK,
+ @GravityInt dialogGravity: Int? = null,
): ComponentSystemUIDialog {
Assert.isMainThread()
@@ -59,6 +63,7 @@ constructor(
sysUiState,
broadcastDispatcher,
dialogTransitionAnimator,
+ dialogGravity,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index 1c33d3fd0288..bef6b0ba483f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -18,15 +18,22 @@ package com.android.systemui.statusbar.pipeline.satellite.ui.viewmodel
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.dagger.OemSatelliteInputLog
import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.DeviceBasedSatelliteInteractor
import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
import javax.inject.Inject
+import kotlin.time.Duration.Companion.seconds
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.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
@@ -42,25 +49,44 @@ constructor(
interactor: DeviceBasedSatelliteInteractor,
@Application scope: CoroutineScope,
airplaneModeRepository: AirplaneModeRepository,
+ @OemSatelliteInputLog logBuffer: LogBuffer,
) {
- private val shouldShowIcon: StateFlow<Boolean> =
- interactor.areAllConnectionsOutOfService
- .flatMapLatest { allOos ->
- if (!allOos) {
- flowOf(false)
+ private val shouldShowIcon: Flow<Boolean> =
+ interactor.areAllConnectionsOutOfService.flatMapLatest { allOos ->
+ if (!allOos) {
+ flowOf(false)
+ } else {
+ combine(interactor.isSatelliteAllowed, airplaneModeRepository.isAirplaneMode) {
+ isSatelliteAllowed,
+ isAirplaneMode ->
+ isSatelliteAllowed && !isAirplaneMode
+ }
+ }
+ }
+
+ // This adds a 10 seconds delay before showing the icon
+ private val shouldActuallyShowIcon: StateFlow<Boolean> =
+ shouldShowIcon
+ .distinctUntilChanged()
+ .flatMapLatest { shouldShow ->
+ if (shouldShow) {
+ logBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ { long1 = DELAY_DURATION.inWholeSeconds },
+ { "Waiting $long1 seconds before showing the satellite icon" }
+ )
+ delay(DELAY_DURATION)
+ flowOf(true)
} else {
- combine(interactor.isSatelliteAllowed, airplaneModeRepository.isAirplaneMode) {
- isSatelliteAllowed,
- isAirplaneMode ->
- isSatelliteAllowed && !isAirplaneMode
- }
+ flowOf(false)
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
val icon: StateFlow<Icon?> =
combine(
- shouldShowIcon,
+ shouldActuallyShowIcon,
interactor.connectionState,
interactor.signalStrength,
) { shouldShow, state, signalStrength ->
@@ -71,4 +97,9 @@ constructor(
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), null)
+
+ companion object {
+ private const val TAG = "DeviceBasedSatelliteViewModel"
+ private val DELAY_DURATION = 10.seconds
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index 7b652c11e9ec..6124f6383fff 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -23,11 +23,13 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
+import com.android.systemui.Flags;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.BroadcastRunning;
import com.android.systemui.dagger.qualifiers.LongRunning;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.NotifInflation;
import dagger.Module;
import dagger.Provides;
@@ -50,6 +52,8 @@ public abstract class SysUIConcurrencyModule {
private static final Long LONG_SLOW_DELIVERY_THRESHOLD = 2500L;
private static final Long BROADCAST_SLOW_DISPATCH_THRESHOLD = 1000L;
private static final Long BROADCAST_SLOW_DELIVERY_THRESHOLD = 1000L;
+ private static final Long NOTIFICATION_INFLATION_SLOW_DISPATCH_THRESHOLD = 1000L;
+ private static final Long NOTIFICATION_INFLATION_SLOW_DELIVERY_THRESHOLD = 1000L;
/** Background Looper */
@Provides
@@ -90,6 +94,24 @@ public abstract class SysUIConcurrencyModule {
return thread.getLooper();
}
+ /** Notification inflation Looper */
+ @Provides
+ @SysUISingleton
+ @NotifInflation
+ public static Looper provideNotifInflationLooper(@Background Looper bgLooper) {
+ if (!Flags.dedicatedNotifInflationThread()) {
+ return bgLooper;
+ }
+
+ final HandlerThread thread = new HandlerThread("NotifInflation",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ final Looper looper = thread.getLooper();
+ looper.setSlowLogThresholdMs(NOTIFICATION_INFLATION_SLOW_DISPATCH_THRESHOLD,
+ NOTIFICATION_INFLATION_SLOW_DELIVERY_THRESHOLD);
+ return looper;
+ }
+
/**
* Background Handler.
*
@@ -225,4 +247,12 @@ public abstract class SysUIConcurrencyModule {
thread.start();
return new Handler(thread.getLooper());
}
+
+ /** */
+ @Provides
+ @SysUISingleton
+ @NotifInflation
+ public static Executor provideNotifInflationExecutor(@NotifInflation Looper looper) {
+ return new ExecutorImpl(looper);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index d47413faeadf..bb907cc0055e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -245,6 +245,29 @@ fun <T> Flow<T>.throttle(periodMs: Long, clock: SystemClock = SystemClockImpl())
}
}
+inline fun <T1, T2, T3, T4, T5, T6, R> combine(
+ flow: Flow<T1>,
+ flow2: Flow<T2>,
+ flow3: Flow<T3>,
+ flow4: Flow<T4>,
+ flow5: Flow<T5>,
+ flow6: Flow<T6>,
+ crossinline transform: suspend (T1, T2, T3, T4, T5, T6) -> R
+): Flow<R> {
+ return kotlinx.coroutines.flow.combine(flow, flow2, flow3, flow4, flow5, flow6) {
+ args: Array<*> ->
+ @Suppress("UNCHECKED_CAST")
+ transform(
+ args[0] as T1,
+ args[1] as T2,
+ args[2] as T3,
+ args[3] as T4,
+ args[4] as T5,
+ args[5] as T6,
+ )
+ }
+}
+
inline fun <T1, T2, T3, T4, T5, T6, T7, R> combine(
flow: Flow<T1>,
flow2: Flow<T2>,
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
index fa0d0306f157..a88be065d722 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
@@ -36,6 +36,17 @@ class Utils {
fun <A, B, C, D, E, F> toSextuple(a: A, bcdef: Quint<B, C, D, E, F>) =
Sextuple(a, bcdef.first, bcdef.second, bcdef.third, bcdef.fourth, bcdef.fifth)
+ fun <A, B, C, D, E, F, G> toSeptuple(a: A, bcdefg: Sextuple<B, C, D, E, F, G>) =
+ Septuple(
+ a,
+ bcdefg.first,
+ bcdefg.second,
+ bcdefg.third,
+ bcdefg.fourth,
+ bcdefg.fifth,
+ bcdefg.sixth
+ )
+
/**
* Samples the provided flows, emitting a tuple of the original flow's value as well as each
* of the combined flows' values.
@@ -90,6 +101,24 @@ class Utils {
): Flow<Sextuple<A, B, C, D, E, F>> {
return this.sample(combine(b, c, d, e, f, ::Quint), ::toSextuple)
}
+
+ /**
+ * Samples the provided flows, emitting a tuple of the original flow's value as well as each
+ * of the combined flows' values.
+ *
+ * Flow<A>.sample(Flow<B>, Flow<C>, Flow<D>, Flow<E>, Flow<F>, Flow<G>) -> (A, B, C, D, E,
+ * F, G)
+ */
+ fun <A, B, C, D, E, F, G> Flow<A>.sample(
+ b: Flow<B>,
+ c: Flow<C>,
+ d: Flow<D>,
+ e: Flow<E>,
+ f: Flow<F>,
+ g: Flow<G>,
+ ): Flow<Septuple<A, B, C, D, E, F, G>> {
+ return this.sample(combine(b, c, d, e, f, g, ::Sextuple), ::toSeptuple)
+ }
}
}
@@ -112,6 +141,16 @@ data class Sextuple<A, B, C, D, E, F>(
val sixth: F,
)
+data class Septuple<A, B, C, D, E, F, G>(
+ val first: A,
+ val second: B,
+ val third: C,
+ val fourth: D,
+ val fifth: E,
+ val sixth: F,
+ val seventh: G,
+)
+
fun Int.toPx(context: Context): Int {
return (this * context.resources.displayMetrics.density).toInt()
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OWNERS b/packages/SystemUI/src/com/android/systemui/volume/OWNERS
index e627d610dc0a..4d2b639ba8fb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/volume/OWNERS
@@ -1,4 +1,6 @@
-asc@google.com # send reviews here
+ethibodeau@google.com
+michaelmikhil@google.com
+apotapov@google.com
+asc@google.com
-juliacr@google.com
-tsuji@google.com
+juliacr@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AncModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AncModule.kt
new file mode 100644
index 000000000000..66df45ca4cfa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AncModule.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dagger
+
+import android.content.Context
+import androidx.slice.SliceViewManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.volume.panel.component.anc.data.repository.AncSliceRepository
+import com.android.systemui.volume.panel.component.anc.data.repository.AncSliceRepositoryImpl
+import dagger.Module
+import dagger.Provides
+
+/** Dagger module that provides ANC controlling backend. */
+@Module
+interface AncModule {
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ fun provideAncSliceRepository(
+ @Application context: Context,
+ implFactory: AncSliceRepositoryImpl.Factory
+ ): AncSliceRepository = implFactory.create(SliceViewManager.getInstance(context))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
index 67d6a7f5d735..8431fbcd8bad 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -16,19 +16,18 @@
package com.android.systemui.volume.dagger
-import android.app.NotificationManager
import android.content.Context
import android.media.AudioManager
import com.android.settingslib.media.data.repository.SpatializerRepository
import com.android.settingslib.media.data.repository.SpatializerRepositoryImpl
import com.android.settingslib.media.domain.interactor.SpatializerInteractor
-import com.android.settingslib.statusbar.notification.data.repository.NotificationsSoundPolicyRepository
-import com.android.settingslib.statusbar.notification.data.repository.NotificationsSoundPolicyRepositoryImpl
+import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor
import com.android.settingslib.volume.data.repository.AudioRepository
import com.android.settingslib.volume.data.repository.AudioRepositoryImpl
import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiverImpl
+import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiverImpl
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import dagger.Module
@@ -46,11 +45,11 @@ interface AudioModule {
fun provideAudioManagerIntentsReceiver(
@Application context: Context,
@Application coroutineScope: CoroutineScope,
- ): AudioManagerIntentsReceiver = AudioManagerIntentsReceiverImpl(context, coroutineScope)
+ ): AudioManagerEventsReceiver = AudioManagerEventsReceiverImpl(context, coroutineScope)
@Provides
fun provideAudioRepository(
- intentsReceiver: AudioManagerIntentsReceiver,
+ intentsReceiver: AudioManagerEventsReceiver,
audioManager: AudioManager,
@Background coroutineContext: CoroutineContext,
@Application coroutineScope: CoroutineScope,
@@ -62,6 +61,13 @@ interface AudioModule {
AudioModeInteractor(repository)
@Provides
+ fun provideAudioVolumeInteractor(
+ audioRepository: AudioRepository,
+ notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor,
+ ): AudioVolumeInteractor =
+ AudioVolumeInteractor(audioRepository, notificationsSoundPolicyInteractor)
+
+ @Provides
fun provdieSpatializerRepository(
audioManager: AudioManager,
@Background backgroundContext: CoroutineContext,
@@ -71,19 +77,5 @@ interface AudioModule {
@Provides
fun provideSpatializerInetractor(repository: SpatializerRepository): SpatializerInteractor =
SpatializerInteractor(repository)
-
- @Provides
- fun provideNotificationsSoundPolicyRepository(
- context: Context,
- notificationManager: NotificationManager,
- @Background coroutineContext: CoroutineContext,
- @Application coroutineScope: CoroutineScope,
- ): NotificationsSoundPolicyRepository =
- NotificationsSoundPolicyRepositoryImpl(
- context,
- notificationManager,
- coroutineScope,
- coroutineContext,
- )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
index 9f99e9778ef2..d134e60ef72f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
@@ -18,9 +18,11 @@ package com.android.systemui.volume.dagger
import android.media.session.MediaSessionManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepositoryImpl
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.domain.interactor.LocalMediaInteractor
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -44,8 +46,21 @@ interface MediaDevicesModule {
@Provides
@SysUISingleton
+ fun provideLocalMediaRepository(
+ factory: LocalMediaRepositoryFactory
+ ): LocalMediaRepository = factory.create(null)
+
+ @Provides
+ @SysUISingleton
+ fun provideLocalMediaInteractor(
+ repository: LocalMediaRepository,
+ @Application scope: CoroutineScope,
+ ): LocalMediaInteractor = LocalMediaInteractor(repository, scope)
+
+ @Provides
+ @SysUISingleton
fun provideMediaDeviceSessionRepository(
- intentsReceiver: AudioManagerIntentsReceiver,
+ intentsReceiver: AudioManagerEventsReceiver,
mediaSessionManager: MediaSessionManager,
localBluetoothManager: LocalBluetoothManager?,
@Application coroutineScope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index 32856373dbe9..c6aee428ce6a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -57,6 +57,7 @@ import dagger.multibindings.IntoSet;
@Module(
includes = {
AudioModule.class,
+ AncModule.class,
CaptioningModule.class,
MediaDevicesModule.class
},
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
new file mode 100644
index 000000000000..8f18aa8021ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc.data.repository
+
+import android.bluetooth.BluetoothDevice
+import android.net.Uri
+import androidx.slice.Slice
+import androidx.slice.SliceViewManager
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.media.BluetoothMediaDevice
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.slice.sliceForUri
+import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+/** Provides ANC slice data */
+interface AncSliceRepository {
+
+ /**
+ * ANC slice with a given width. Emits null when there is no ANC slice available. This can mean
+ * that:
+ * - there is no supported device connected;
+ * - there is no slice provider for the uri;
+ */
+ fun ancSlice(width: Int): Flow<Slice?>
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class AncSliceRepositoryImpl
+@AssistedInject
+constructor(
+ mediaRepositoryFactory: LocalMediaRepositoryFactory,
+ @Background private val backgroundCoroutineContext: CoroutineContext,
+ @Assisted private val sliceViewManager: SliceViewManager,
+) : AncSliceRepository {
+
+ private val localMediaRepository = mediaRepositoryFactory.create(null)
+
+ override fun ancSlice(width: Int): Flow<Slice?> {
+ return localMediaRepository.currentConnectedDevice
+ .map { (it as? BluetoothMediaDevice)?.cachedDevice?.device?.getExtraControlUri(width) }
+ .distinctUntilChanged()
+ .flatMapLatest { sliceUri ->
+ sliceUri ?: return@flatMapLatest flowOf(null)
+ sliceViewManager.sliceForUri(sliceUri)
+ }
+ .flowOn(backgroundCoroutineContext)
+ }
+
+ private fun BluetoothDevice.getExtraControlUri(width: Int): Uri? {
+ val uri: String? = BluetoothUtils.getControlUriMetaData(this)
+ uri ?: return null
+
+ return if (uri.isEmpty()) {
+ null
+ } else {
+ Uri.parse(
+ "$uri$width" +
+ "&version=${SliceParameters.VERSION}" +
+ "&is_collapsed=${SliceParameters.IS_COLLAPSED}"
+ )
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(sliceViewManager: SliceViewManager): AncSliceRepositoryImpl
+ }
+
+ private object SliceParameters {
+ /**
+ * Slice version
+ * 1) legacy slice
+ * 2) new slice
+ */
+ const val VERSION = 2
+
+ /**
+ * Collapsed slice shows a single button, and expanded shows a row buttons. Supported since
+ * [VERSION]==2.
+ */
+ const val IS_COLLAPSED = false
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt
new file mode 100644
index 000000000000..89b927480783
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc.domain
+
+import com.android.systemui.volume.panel.component.anc.domain.interactor.AncSliceInteractor
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** Determines if ANC component is available for the Volume Panel. */
+@VolumePanelScope
+class AncAvailabilityCriteria
+@Inject
+constructor(
+ private val ancSliceInteractor: AncSliceInteractor,
+) : ComponentAvailabilityCriteria {
+
+ override fun isAvailable(): Flow<Boolean> = ancSliceInteractor.ancSlice.map { it != null }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
new file mode 100644
index 000000000000..91af622074a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc.domain.interactor
+
+import android.app.slice.Slice.HINT_ERROR
+import android.app.slice.SliceItem.FORMAT_SLICE
+import androidx.slice.Slice
+import com.android.systemui.volume.panel.component.anc.data.repository.AncSliceRepository
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.shareIn
+
+/** Provides a valid slice from [AncSliceRepository]. */
+@OptIn(ExperimentalCoroutinesApi::class)
+@VolumePanelScope
+class AncSliceInteractor
+@Inject
+constructor(
+ private val ancSliceRepository: AncSliceRepository,
+ scope: CoroutineScope,
+) {
+
+ // Start with a positive width to check is the Slice is available.
+ private val width = MutableStateFlow(1)
+
+ /** Provides a valid ANC slice. */
+ val ancSlice: SharedFlow<Slice?> =
+ width
+ .flatMapLatest { width -> ancSliceRepository.ancSlice(width) }
+ .map { slice ->
+ if (slice?.isValidSlice() == true) {
+ slice
+ } else {
+ null
+ }
+ }
+ .shareIn(scope, SharingStarted.Eagerly, replay = 1)
+
+ /** Updates the width of the [ancSlice] */
+ fun changeWidth(newWidth: Int) {
+ width.value = newWidth
+ }
+
+ private fun Slice.isValidSlice(): Boolean {
+ if (hints.contains(HINT_ERROR)) {
+ return false
+ }
+ for (item in items) {
+ if (item.format == FORMAT_SLICE) {
+ return true
+ }
+ }
+ return false
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
new file mode 100644
index 000000000000..eb96f6cad8f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc.ui.viewmodel
+
+import android.content.Context
+import androidx.slice.Slice
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
+import com.android.systemui.volume.panel.component.anc.domain.interactor.AncSliceInteractor
+import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/** Volume Panel ANC component view model. */
+@VolumePanelScope
+class AncViewModel
+@Inject
+constructor(
+ @Application private val context: Context,
+ @VolumePanelScope private val coroutineScope: CoroutineScope,
+ private val interactor: AncSliceInteractor,
+) {
+
+ /** ANC [Slice]. Null when there is no slice available for ANC. */
+ val slice: StateFlow<Slice?> =
+ interactor.ancSlice.stateIn(coroutineScope, SharingStarted.Eagerly, null)
+
+ /**
+ * ButtonViewModel to be shown in the VolumePanel. Null when there is no ANC Slice available.
+ */
+ val button: StateFlow<ButtonViewModel?> =
+ interactor.ancSlice
+ .map { slice ->
+ slice?.let {
+ ButtonViewModel(
+ Icon.Resource(R.drawable.ic_noise_aware, null),
+ context.getString(R.string.volume_panel_noise_control_title)
+ )
+ }
+ }
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+
+ /** Call this to update [slice] width in a reaction to container size change. */
+ fun changeSliceWidth(width: Int) {
+ interactor.changeWidth(width)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt
new file mode 100644
index 000000000000..754d258b2b86
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.button.ui.viewmodel
+
+import com.android.systemui.common.shared.model.Icon
+
+/** Models base buttons appearance. */
+data class ButtonViewModel(
+ val icon: Icon,
+ val label: CharSequence,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
index 1f52260bb20d..11b4690e59ee 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
@@ -18,10 +18,10 @@ package com.android.systemui.volume.panel.component.mediaoutput.data.repository
import android.media.MediaRouter2Manager
import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.LocalMediaRepositoryImpl
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.media.controls.pipeline.LocalMediaManagerFactory
+import com.android.systemui.media.controls.util.LocalMediaManagerFactory
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
@@ -34,7 +34,7 @@ interface LocalMediaRepositoryFactory {
class LocalMediaRepositoryFactoryImpl
@Inject
constructor(
- private val intentsReceiver: AudioManagerIntentsReceiver,
+ private val eventsReceiver: AudioManagerEventsReceiver,
private val mediaRouter2Manager: MediaRouter2Manager,
private val localMediaManagerFactory: LocalMediaManagerFactory,
@Application private val coroutineScope: CoroutineScope,
@@ -43,7 +43,7 @@ constructor(
override fun create(packageName: String?): LocalMediaRepository =
LocalMediaRepositoryImpl(
- intentsReceiver,
+ eventsReceiver,
localMediaManagerFactory.create(packageName),
mediaRouter2Manager,
coroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt
index 020ec64c0491..bac7d15235d0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt
@@ -17,27 +17,19 @@
package com.android.systemui.volume.panel.component.mediaoutput.domain
import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
-import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
/** Determines if the Media Output Volume Panel component is available. */
@VolumePanelScope
class MediaOutputAvailabilityCriteria
@Inject
constructor(
- private val mediaOutputInteractor: MediaOutputInteractor,
private val audioModeInteractor: AudioModeInteractor,
) : ComponentAvailabilityCriteria {
- override fun isAvailable(): Flow<Boolean> {
- return combine(mediaOutputInteractor.mediaDevices, audioModeInteractor.isOngoingCall) {
- devices,
- isOngoingCall ->
- !isOngoingCall && devices.isNotEmpty()
- }
- }
+ override fun isAvailable(): Flow<Boolean> = audioModeInteractor.isOngoingCall.map { !it }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index 24cc29d8e1f9..0f5343701ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -95,10 +95,6 @@ constructor(
val currentConnectedDevice: Flow<MediaDevice?> =
localMediaRepository.flatMapLatest { it.currentConnectedDevice }
- /** A list of available [MediaDevice]s. */
- val mediaDevices: Flow<Collection<MediaDevice>> =
- localMediaRepository.flatMapLatest { it.mediaDevices }
-
private suspend fun getApplicationLabel(packageName: String): CharSequence? {
return try {
withContext(backgroundCoroutineContext) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt
index e0718ace2c30..e518ed022792 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt
@@ -23,15 +23,18 @@ import com.android.systemui.common.shared.model.Icon
sealed interface DeviceIconViewModel {
val icon: Icon
+ val iconColor: Color
val backgroundColor: Color
class IsPlaying(
override val icon: Icon,
+ override val iconColor: Color,
override val backgroundColor: Color,
) : DeviceIconViewModel
class IsNotPlaying(
override val icon: Icon,
+ override val iconColor: Color,
override val backgroundColor: Color,
) : DeviceIconViewModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
index d14899294526..85d6c9e341ac 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
@@ -86,13 +86,21 @@ constructor(
null
)
DeviceIconViewModel.IsPlaying(
- icon,
- Color.Attribute(com.android.internal.R.attr.materialColorSecondary),
+ icon = icon,
+ iconColor =
+ Color.Attribute(com.android.internal.R.attr.materialColorSurface),
+ backgroundColor =
+ Color.Attribute(com.android.internal.R.attr.materialColorSecondary),
)
} else {
DeviceIconViewModel.IsNotPlaying(
- Icon.Resource(R.drawable.ic_media_home_devices, null),
- Color.Attribute(com.android.internal.R.attr.materialColorSurface),
+ icon = Icon.Resource(R.drawable.ic_media_home_devices, null),
+ iconColor =
+ Color.Attribute(
+ com.android.internal.R.attr.materialColorOnSurfaceVariant
+ ),
+ backgroundColor =
+ Color.Attribute(com.android.internal.R.attr.materialColorSurface),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt
index 6c742ba7e5f9..f11ac5e1a8e4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt
@@ -23,4 +23,5 @@ object VolumePanelComponents {
const val MEDIA_OUTPUT: VolumePanelComponentKey = "media_output"
const val BOTTOM_BAR: VolumePanelComponentKey = "bottom_bar"
const val CAPTIONING: VolumePanelComponentKey = "captioning"
+ const val ANC: VolumePanelComponentKey = "anc"
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/CastVolumeInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/CastVolumeInteractor.kt
new file mode 100644
index 000000000000..6b62074e023d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/CastVolumeInteractor.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.volume.domain.interactor
+
+import com.android.settingslib.volume.domain.interactor.LocalMediaInteractor
+import com.android.settingslib.volume.domain.model.RoutingSession
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/** Provides a remote media casting state. */
+@VolumePanelScope
+class CastVolumeInteractor
+@Inject
+constructor(
+ @VolumePanelScope private val coroutineScope: CoroutineScope,
+ private val localMediaInteractor: LocalMediaInteractor,
+) {
+
+ /** Returns a list of [RoutingSession] to show in the UI. */
+ val remoteRoutingSessions: StateFlow<List<RoutingSession>> =
+ localMediaInteractor.remoteRoutingSessions
+ .map { it.filter { routingSession -> routingSession.isVolumeSeekBarEnabled } }
+ .stateIn(coroutineScope, SharingStarted.Eagerly, emptyList())
+
+ /** Sets [routingSession] volume to [volume]. */
+ suspend fun setVolume(routingSession: RoutingSession, volume: Int) {
+ localMediaInteractor.adjustSessionVolume(routingSession.routingSessionInfo.id, volume)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractor.kt
new file mode 100644
index 000000000000..52736c6cb08b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractor.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.volume.domain.interactor
+
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+
+/** Converts from slider value to volume and back. */
+@VolumePanelScope
+class VolumeSliderInteractor @Inject constructor() {
+
+ /** mimic percentage volume setting */
+ private val displayValueRange: ClosedFloatingPointRange<Float> = 0f..100f
+
+ /**
+ * Translates [volume], that belongs to [volumeRange] to the value that belongs to
+ * [displayValueRange].
+ *
+ * [currentValue] is the raw value received from the slider. Returns [currentValue] when it
+ * translates to the same volume as [volume] parameter. This ensures smooth slider experience
+ * (avoids snapping when the user stops dragging).
+ */
+ fun processVolumeToValue(
+ volume: Int,
+ volumeRange: ClosedRange<Int>,
+ currentValue: Float?,
+ isMuted: Boolean,
+ ): Float {
+ if (isMuted) {
+ return 0f
+ }
+ val changedVolume: Int? = currentValue?.let { translateValueToVolume(it, volumeRange) }
+ return if (volume != volumeRange.start && volume == changedVolume) {
+ currentValue
+ } else {
+ translateToRange(
+ currentValue = volume.toFloat(),
+ currentRangeStart = volumeRange.start.toFloat(),
+ currentRangeEnd = volumeRange.endInclusive.toFloat(),
+ targetRangeStart = displayValueRange.start,
+ targetRangeEnd = displayValueRange.endInclusive,
+ )
+ }
+ }
+
+ /** Translates [value] from [displayValueRange] to volume that has [volumeRange]. */
+ fun translateValueToVolume(
+ value: Float,
+ volumeRange: ClosedRange<Int>,
+ ): Int {
+ return translateToRange(
+ currentValue = value,
+ currentRangeStart = displayValueRange.start,
+ currentRangeEnd = displayValueRange.endInclusive,
+ targetRangeStart = volumeRange.start.toFloat(),
+ targetRangeEnd = volumeRange.endInclusive.toFloat(),
+ )
+ .toInt()
+ }
+
+ /**
+ * Translates a value from one range to another.
+ *
+ * ```
+ * Given: currentValue=3, currentRange=[0, 8], targetRange=[0, 100]
+ * Result: 37.5
+ * ```
+ */
+ private fun translateToRange(
+ currentValue: Float,
+ currentRangeStart: Float,
+ currentRangeEnd: Float,
+ targetRangeStart: Float,
+ targetRangeEnd: Float,
+ ): Float {
+ val currentRangeLength: Float = (currentRangeEnd - currentRangeStart)
+ val targetRangeLength: Float = targetRangeEnd - targetRangeStart
+ if (currentRangeLength == 0f || targetRangeLength == 0f) {
+ return 0f
+ }
+ val volumeFraction: Float = (currentValue - currentRangeStart) / currentRangeLength
+ return targetRangeStart + volumeFraction * targetRangeLength
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/model/SliderType.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/model/SliderType.kt
new file mode 100644
index 000000000000..b97123b29b68
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/model/SliderType.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.volume.domain.model
+
+import com.android.settingslib.volume.shared.model.AudioStream
+
+/** The type of volume slider that can be shown at the UI. */
+sealed interface SliderType {
+
+ /** The slider represents one of the device volume streams. */
+ data class Stream(val stream: AudioStream) : SliderType
+
+ /** The represents media device casting volume. */
+ data object MediaDeviceCast : SliderType
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
index afd3f6170d3d..df4972aeafea 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
@@ -16,6 +16,7 @@
package com.android.systemui.volume.panel.dagger
+import com.android.systemui.volume.panel.component.anc.AncModule
import com.android.systemui.volume.panel.component.bottombar.BottomBarModule
import com.android.systemui.volume.panel.component.captioning.CaptioningModule
import com.android.systemui.volume.panel.component.mediaoutput.MediaOutputModule
@@ -46,6 +47,7 @@ import kotlinx.coroutines.CoroutineScope
UiModule::class,
// Components modules
BottomBarModule::class,
+ AncModule::class,
CaptioningModule::class,
MediaOutputModule::class,
]
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt
index 55d8de5aeb95..0d65c429eac6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt
@@ -50,6 +50,7 @@ interface DomainModule {
@VolumePanelScope
fun provideEnabledComponents(): Collection<VolumePanelComponentKey> {
return setOf(
+ VolumePanelComponents.ANC,
VolumePanelComponents.CAPTIONING,
VolumePanelComponents.MEDIA_OUTPUT,
VolumePanelComponents.BOTTOM_BAR,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
index 867df4a87dd5..ec4da0692841 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
@@ -47,7 +47,8 @@ interface UiModule {
@VolumePanelScope
@FooterComponents
fun provideFooterComponents(): Collection<VolumePanelComponentKey> {
- return setOf(
+ return listOf(
+ VolumePanelComponents.ANC,
VolumePanelComponents.CAPTIONING,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderImplTest.kt
index a19a0c7d12a3..d2a17c2ccbb4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderImplTest.kt
@@ -89,7 +89,7 @@ class CameraProtectionLoaderImplTest : SysuiTestCase() {
loader.loadCameraProtectionInfoList().map { it.toTestableVersion() }
private fun CameraProtectionInfo.toTestableVersion() =
- TestableProtectionInfo(logicalCameraId, physicalCameraId, cutoutBounds, displayUniqueId)
+ TestableProtectionInfo(logicalCameraId, physicalCameraId, bounds, displayUniqueId)
/**
* "Testable" version, because the original version contains a Path property, which doesn't
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt b/packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt
index f769b4e5f2d0..6cb77cff4404 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt
@@ -16,6 +16,7 @@
package com.android.systemui
+import android.graphics.Rect
import com.android.systemui.res.R
class FakeCameraProtectionLoader(private val context: SysuiTestableContext) :
@@ -36,7 +37,10 @@ class FakeCameraProtectionLoader(private val context: SysuiTestableContext) :
addInnerCameraProtection()
}
- fun addOuterCameraProtection(displayUniqueId: String = "111") {
+ fun addOuterCameraProtection(
+ displayUniqueId: String = "111",
+ bounds: Rect = Rect(/* left = */ 0, /* top = */ 0, /* right = */ 10, /* bottom = */ 10)
+ ) {
context.orCreateTestableResources.addOverride(R.string.config_protectedCameraId, "1")
context.orCreateTestableResources.addOverride(
R.string.config_protectedPhysicalCameraId,
@@ -44,7 +48,7 @@ class FakeCameraProtectionLoader(private val context: SysuiTestableContext) :
)
context.orCreateTestableResources.addOverride(
R.string.config_frontBuiltInDisplayCutoutProtection,
- "M 0,0 H 10,10 V 10,10 H 0,10 Z"
+ bounds.asPath(),
)
context.orCreateTestableResources.addOverride(
R.string.config_protectedScreenUniqueId,
@@ -52,7 +56,10 @@ class FakeCameraProtectionLoader(private val context: SysuiTestableContext) :
)
}
- fun addInnerCameraProtection(displayUniqueId: String = "222") {
+ fun addInnerCameraProtection(
+ displayUniqueId: String = "222",
+ bounds: Rect = Rect(/* left = */ 0, /* top = */ 0, /* right = */ 20, /* bottom = */ 20)
+ ) {
context.orCreateTestableResources.addOverride(R.string.config_protectedInnerCameraId, "2")
context.orCreateTestableResources.addOverride(
R.string.config_protectedInnerPhysicalCameraId,
@@ -60,11 +67,13 @@ class FakeCameraProtectionLoader(private val context: SysuiTestableContext) :
)
context.orCreateTestableResources.addOverride(
R.string.config_innerBuiltInDisplayCutoutProtection,
- "M 0,0 H 20,20 V 20,20 H 0,20 Z"
+ bounds.asPath(),
)
context.orCreateTestableResources.addOverride(
R.string.config_protectedInnerScreenUniqueId,
displayUniqueId
)
}
+
+ private fun Rect.asPath() = "M $left, $top H $right V $bottom H $left Z"
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysUICutoutProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/SysUICutoutProviderTest.kt
index f37c4ae613ff..61c7e1d63e51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysUICutoutProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysUICutoutProviderTest.kt
@@ -16,11 +16,16 @@
package com.android.systemui
+import android.graphics.Rect
import android.view.Display
import android.view.DisplayAdjustments
import android.view.DisplayCutout
+import android.view.DisplayInfo
+import android.view.Surface
+import android.view.Surface.Rotation
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -39,7 +44,7 @@ class SysUICutoutProviderTest : SysuiTestCase() {
val noCutoutDisplayContext = context.createDisplayContext(noCutoutDisplay)
val provider = SysUICutoutProvider(noCutoutDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()
assertThat(sysUICutout).isNull()
}
@@ -50,7 +55,7 @@ class SysUICutoutProviderTest : SysuiTestCase() {
val cutoutDisplayContext = context.createDisplayContext(cutoutDisplay)
val provider = SysUICutoutProvider(cutoutDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cutout).isEqualTo(cutoutDisplay.cutout)
}
@@ -61,7 +66,7 @@ class SysUICutoutProviderTest : SysuiTestCase() {
val cutoutDisplayContext = context.createDisplayContext(cutoutDisplay)
val provider = SysUICutoutProvider(cutoutDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNull()
}
@@ -72,7 +77,7 @@ class SysUICutoutProviderTest : SysuiTestCase() {
val outerDisplayContext = context.createDisplayContext(OUTER_DISPLAY)
val provider = SysUICutoutProvider(outerDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNotNull()
}
@@ -83,7 +88,7 @@ class SysUICutoutProviderTest : SysuiTestCase() {
val outerDisplayContext = context.createDisplayContext(OUTER_DISPLAY)
val provider = SysUICutoutProvider(outerDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNull()
}
@@ -94,7 +99,7 @@ class SysUICutoutProviderTest : SysuiTestCase() {
val displayContext = context.createDisplayContext(createDisplay(uniqueId = null))
val provider = SysUICutoutProvider(displayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNull()
}
@@ -105,20 +110,170 @@ class SysUICutoutProviderTest : SysuiTestCase() {
val displayContext = context.createDisplayContext(createDisplay(uniqueId = ""))
val provider = SysUICutoutProvider(displayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNull()
}
+ @Test
+ fun cutoutInfo_rotation0_returnsOriginalProtectionBounds() {
+ val provider =
+ setUpProviderWithCameraProtection(
+ displayWidth = 500,
+ displayHeight = 1000,
+ rotation = Surface.ROTATION_0,
+ protectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+
+ assertThat(sysUICutout.cameraProtection!!.bounds)
+ .isEqualTo(
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+ }
+
+ @Test
+ fun cutoutInfo_rotation90_returnsRotatedProtectionBounds() {
+ val provider =
+ setUpProviderWithCameraProtection(
+ displayWidth = 500,
+ displayHeight = 1000,
+ rotation = Surface.ROTATION_90,
+ protectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+
+ assertThat(sysUICutout.cameraProtection!!.bounds)
+ .isEqualTo(Rect(/* left = */ 10, /* top = */ 10, /* right = */ 110, /* bottom = */ 60))
+ }
+
+ @Test
+ fun cutoutInfo_withRotation_doesNotMutateOriginalBounds() {
+ val displayNaturalWidth = 500
+ val displayNaturalHeight = 1000
+ val originalProtectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ // Safe copy as we don't know at which layer the mutation could happen
+ val originalProtectionBoundsCopy = Rect(originalProtectionBounds)
+ val display =
+ createDisplay(
+ uniqueId = OUTER_DISPLAY_UNIQUE_ID,
+ rotation = Surface.ROTATION_180,
+ width = displayNaturalWidth,
+ height = displayNaturalHeight,
+ )
+ fakeProtectionLoader.addOuterCameraProtection(
+ displayUniqueId = OUTER_DISPLAY_UNIQUE_ID,
+ bounds = originalProtectionBounds
+ )
+ val provider =
+ SysUICutoutProvider(context.createDisplayContext(display), fakeProtectionLoader)
+
+ // Here we get the rotated bounds once
+ provider.cutoutInfoForCurrentDisplayAndRotation()
+
+ // Rotate display back to original rotation
+ whenever(display.rotation).thenReturn(Surface.ROTATION_0)
+
+ // Now the bounds should match the original ones. We are checking for mutation since Rect
+ // is mutable and has many methods that mutate the instance, and it is easy to do it by
+ // mistake.
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+ assertThat(sysUICutout.cameraProtection!!.bounds).isEqualTo(originalProtectionBoundsCopy)
+ }
+
+ @Test
+ fun cutoutInfo_rotation180_returnsRotatedProtectionBounds() {
+ val provider =
+ setUpProviderWithCameraProtection(
+ displayWidth = 500,
+ displayHeight = 1000,
+ rotation = Surface.ROTATION_180,
+ protectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+
+ assertThat(sysUICutout.cameraProtection!!.bounds)
+ .isEqualTo(Rect(/* left = */ 10, /* top = */ 890, /* right = */ 60, /* bottom = */ 990))
+ }
+
+ @Test
+ fun cutoutInfo_rotation270_returnsRotatedProtectionBounds() {
+ val provider =
+ setUpProviderWithCameraProtection(
+ displayWidth = 500,
+ displayHeight = 1000,
+ rotation = Surface.ROTATION_270,
+ protectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+
+ assertThat(sysUICutout.cameraProtection!!.bounds)
+ .isEqualTo(
+ Rect(/* left = */ 890, /* top = */ 440, /* right = */ 990, /* bottom = */ 490)
+ )
+ }
+
+ private fun setUpProviderWithCameraProtection(
+ displayWidth: Int,
+ displayHeight: Int,
+ rotation: Int = Surface.ROTATION_0,
+ protectionBounds: Rect,
+ ): SysUICutoutProvider {
+ val display =
+ createDisplay(
+ uniqueId = OUTER_DISPLAY_UNIQUE_ID,
+ rotation = rotation,
+ width =
+ if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
+ displayWidth
+ } else {
+ displayHeight
+ },
+ height =
+ if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180)
+ displayHeight
+ else displayWidth,
+ )
+ fakeProtectionLoader.addOuterCameraProtection(
+ displayUniqueId = OUTER_DISPLAY_UNIQUE_ID,
+ bounds = protectionBounds
+ )
+ return SysUICutoutProvider(context.createDisplayContext(display), fakeProtectionLoader)
+ }
+
companion object {
private const val OUTER_DISPLAY_UNIQUE_ID = "outer"
private val OUTER_DISPLAY = createDisplay(uniqueId = OUTER_DISPLAY_UNIQUE_ID)
private fun createDisplay(
+ width: Int = 500,
+ height: Int = 1000,
+ @Rotation rotation: Int = Surface.ROTATION_0,
uniqueId: String? = "uniqueId",
cutout: DisplayCutout? = mock<DisplayCutout>()
) =
mock<Display> {
+ whenever(this.getDisplayInfo(any())).thenAnswer {
+ val displayInfo = it.arguments[0] as DisplayInfo
+ displayInfo.rotation = rotation
+ displayInfo.logicalWidth = width
+ displayInfo.logicalHeight = height
+ return@thenAnswer true
+ }
+ // Setting width and height to smaller values to simulate real behavior of this API
+ // not always returning the real display size
+ whenever(this.width).thenReturn(width - 5)
+ whenever(this.height).thenReturn(height - 10)
+ whenever(this.rotation).thenReturn(rotation)
whenever(this.displayAdjustments).thenReturn(DisplayAdjustments())
whenever(this.cutout).thenReturn(cutout)
whenever(this.uniqueId).thenReturn(uniqueId)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 0b0410aa1670..0d464cfd71f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -180,6 +180,17 @@ public class AppOpsControllerTest extends SysuiTestCase {
assertThat(mController.getActiveAppOps()).isEmpty();
}
+ /** Regression test for b/324329757 */
+ @Test
+ public void startListening_fetchCurrentActive_nullPackageOps() {
+ when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS)).thenReturn(null);
+
+ mController.setListening(true);
+ mBgExecutor.runAllReady();
+
+ assertThat(mController.getActiveAppOps()).isEmpty();
+ }
+
/** Regression test for b/294104969. */
@Test
public void startListening_fetchesCurrentActive_oneActive() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
index f770a3885aad..6e00b70b5410 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -21,6 +21,8 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
@@ -40,6 +42,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -71,6 +75,10 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
private KeyguardStateController mKeyguardStateController;
@Mock
private AccessibilityManager mAccessibilityManager;
+ @Captor
+ private ArgumentCaptor<FalsingDataProvider.SessionListener> mSessionListenerArgumentCaptor;
+ @Captor
+ private ArgumentCaptor<HistoryTracker.BeliefListener> mBeliefListenerArgumentCaptor;
private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1);
private final FalsingClassifier.Result mFalsedResult =
@@ -194,4 +202,28 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
when(mFalsingDataProvider.isFromTrackpad()).thenReturn(true);
assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
}
+
+ @Test
+ public void testAddAndRemoveFalsingBeliefListener() {
+ verify(mHistoryTracker, never()).addBeliefListener(any());
+
+ // Session started
+ final FalsingDataProvider.SessionListener sessionListener = captureSessionListener();
+ sessionListener.onSessionStarted();
+
+ // Verify belief listener added when session started
+ verify(mHistoryTracker).addBeliefListener(mBeliefListenerArgumentCaptor.capture());
+ verify(mHistoryTracker, never()).removeBeliefListener(any());
+
+ // Session ended
+ sessionListener.onSessionEnded();
+
+ // Verify belief listener removed when session ended
+ verify(mHistoryTracker).removeBeliefListener(mBeliefListenerArgumentCaptor.getValue());
+ }
+
+ private FalsingDataProvider.SessionListener captureSessionListener() {
+ verify(mFalsingDataProvider).addSessionListener(mSessionListenerArgumentCaptor.capture());
+ return mSessionListenerArgumentCaptor.getValue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index fcb18f52086d..3f13033217b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -85,6 +86,8 @@ public class FalsingCollectorImplTest extends SysuiTestCase {
private BatteryController mBatteryController;
@Mock
private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
+ private CommunalInteractor mCommunalInteractor;
private final DockManagerFake mDockManager = new DockManagerFake();
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
@@ -102,7 +105,8 @@ public class FalsingCollectorImplTest extends SysuiTestCase {
mStatusBarStateController, mKeyguardStateController,
() -> mShadeInteractor, mBatteryController,
mDockManager, mFakeExecutor,
- mJavaAdapter, mFakeSystemClock, () -> mSelectedUserInteractor
+ mJavaAdapter, mFakeSystemClock, () -> mSelectedUserInteractor,
+ () -> mCommunalInteractor
);
mFalsingCollector.init();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
index 2bf9ab264bcf..05b4a41d1b40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
@@ -37,7 +37,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.complication.dagger.DreamMediaEntryComplicationComponent;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.media.controls.ui.MediaCarouselController;
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController;
import com.android.systemui.media.dream.MediaDreamComplication;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt
index a53f8d43f323..5581f0c0314f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt
@@ -23,6 +23,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.biometrics.domain.faceHelpMessageDeferral
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.coroutines.collectLastValue
@@ -39,11 +40,13 @@ import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationS
import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -57,6 +60,7 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
private val fingerprintAuthRepository = kosmos.deviceEntryFingerprintAuthRepository
private val faceAuthRepository = kosmos.fakeDeviceEntryFaceAuthRepository
private val biometricSettingsRepository = kosmos.biometricSettingsRepository
+ private val faceHelpMessageDeferral = kosmos.faceHelpMessageDeferral
@Test
fun fingerprintErrorMessage() =
@@ -266,11 +270,38 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
)
)
- // THEN fingerprintFailedMessage is updated
+ // THEN fingerprintHelpMessage is updated
assertThat(faceHelpMessage).isNotNull()
}
@Test
+ fun faceHelpMessageShouldDefer() =
+ testScope.runTest {
+ val faceHelpMessage by collectLastValue(underTest.faceMessage)
+
+ // GIVEN face is allowed
+ biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
+
+ // GIVEN face only enrolled
+ biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+
+ // WHEN all face help messages should be deferred
+ whenever(faceHelpMessageDeferral.shouldDefer(anyInt())).thenReturn(true)
+
+ // WHEN authentication status help
+ faceAuthRepository.setAuthenticationStatus(
+ HelpFaceAuthenticationStatus(
+ msg = "Move left",
+ msgId = FaceManager.FACE_ACQUIRED_TOO_RIGHT,
+ )
+ )
+
+ // THEN fingerprintHelpMessage is NOT updated
+ assertThat(faceHelpMessage).isNull()
+ }
+
+ @Test
fun faceHelpMessage_coEx() =
testScope.runTest {
val faceHelpMessage by collectLastValue(underTest.faceMessage)
@@ -291,7 +322,7 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
)
)
- // THEN fingerprintFailedMessage is NOT updated
+ // THEN fingerprintHelpMessage is NOT updated
assertThat(faceHelpMessage).isNull()
}
@@ -337,7 +368,7 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
testScope.runTest {
val faceErrorMessage by collectLastValue(underTest.faceMessage)
- // WHEN authentication status error is FACE_ERROR_HW_UNAVAILABLE
+ // WHEN authentication status error is FACE_ERROR_TIMEOUT
faceAuthRepository.setAuthenticationStatus(
ErrorFaceAuthenticationStatus(msgId = FaceManager.FACE_ERROR_TIMEOUT, msg = "test")
)
@@ -349,4 +380,23 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
assertThat(faceErrorMessage).isInstanceOf(FaceTimeoutMessage::class.java)
assertThat(faceErrorMessage?.message).isEqualTo("test")
}
+
+ @Test
+ fun faceTimeoutDeferredErrorMessage() =
+ testScope.runTest {
+ whenever(faceHelpMessageDeferral.getDeferredMessage()).thenReturn("deferredMessage")
+ val faceErrorMessage by collectLastValue(underTest.faceMessage)
+
+ // WHEN authentication status error is FACE_ERROR_TIMEOUT
+ faceAuthRepository.setAuthenticationStatus(
+ ErrorFaceAuthenticationStatus(msgId = FaceManager.FACE_ERROR_TIMEOUT, msg = "test")
+ )
+
+ // GIVEN face is allowed
+ biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
+
+ // THEN faceErrorMessage is updated to deferred message instead of timeout message
+ assertThat(faceErrorMessage).isNotInstanceOf(FaceTimeoutMessage::class.java)
+ assertThat(faceErrorMessage?.message).isEqualTo("deferredMessage")
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 3f454925186c..77b30402c040 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -65,6 +65,8 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.globalactions.domain.interactor.GlobalActionsInteractor;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserTracker;
@@ -140,6 +142,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
private TestableLooper mTestableLooper;
+ private KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
+ private GlobalActionsInteractor mInteractor;
@Before
public void setUp() throws Exception {
@@ -154,6 +158,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mGlobalSettings = new FakeGlobalSettings();
mSecureSettings = new FakeSettings();
+ mInteractor = mKosmos.getGlobalActionsInteractor();
mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext,
mWindowManagerFuncs,
@@ -189,7 +194,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mShadeController,
mKeyguardUpdateMonitor,
mDialogTransitionAnimator,
- mSelectedUserInteractor);
+ mSelectedUserInteractor,
+ mInteractor);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
@@ -623,6 +629,18 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
assertThat(bugReportAction.showBeforeProvisioning()).isFalse();
}
+ @Test
+ public void testInteractor_onShow() {
+ mGlobalActionsDialogLite.onShow(null);
+ assertThat(mInteractor.isVisible().getValue()).isTrue();
+ }
+
+ @Test
+ public void testInteractor_onDismiss() {
+ mGlobalActionsDialogLite.onDismiss(mGlobalActionsDialogLite.mDialog);
+ assertThat(mInteractor.isVisible().getValue()).isFalse();
+ }
+
private UserInfo mockCurrentUser(int flags) {
return new UserInfo(10, "A User", flags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryTest.kt
new file mode 100644
index 000000000000..e437c10c7b73
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.globalactions.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GlobalActionsRepositoryTest : SysuiTestCase() {
+ private lateinit var underTest: GlobalActionsRepository
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ @Before
+ fun setUp() {
+ underTest = kosmos.globalActionsRepository
+ }
+
+ @Test
+ fun isVisible_initialValueFalse() {
+ testScope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ runCurrent()
+
+ assertThat(isVisible).isFalse()
+ }
+ }
+
+ @Test
+ fun isVisible_onChange() {
+ testScope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ runCurrent()
+
+ underTest.setVisible(true)
+ assertThat(isVisible).isTrue()
+
+ underTest.setVisible(false)
+ assertThat(isVisible).isFalse()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorTest.kt
new file mode 100644
index 000000000000..9275512009b2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.globalactions.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GlobalActionsInteractorTest : SysuiTestCase() {
+ private lateinit var underTest: GlobalActionsInteractor
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ @Before
+ fun setup() {
+ underTest = kosmos.globalActionsInteractor
+ }
+
+ @Test
+ fun OnDismissed() {
+ testScope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ underTest.onDismissed()
+ runCurrent()
+
+ assertThat(isVisible).isFalse()
+ }
+ }
+
+ @Test
+ fun OnShown() {
+ testScope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ underTest.onShown()
+ runCurrent()
+
+ assertThat(isVisible).isTrue()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index cf8fe79f70f0..2b51863117e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -2,6 +2,9 @@ package com.android.systemui.keyguard
import android.content.ComponentCallbacks2
import android.graphics.HardwareRenderer
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -23,6 +26,7 @@ import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -48,10 +52,11 @@ class ResourceTrimmerTest : SysuiTestCase() {
@Mock private lateinit var globalWindowManager: GlobalWindowManager
private lateinit var resourceTrimmer: ResourceTrimmer
+ @Rule @JvmField public val setFlagsRule = SetFlagsRule()
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- featureFlags.set(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK, true)
featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, true)
keyguardRepository.setDozeAmount(0f)
keyguardRepository.setKeyguardGoingAway(false)
@@ -76,6 +81,7 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun noChange_noOutputChanges() =
testScope.runTest {
testScope.runCurrent()
@@ -83,6 +89,7 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun dozeAodDisabled_sleep_trimsMemory() =
testScope.runTest {
powerInteractor.setAsleepForTest()
@@ -93,6 +100,27 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
+ fun dozeAodDisabled_flagDisabled_sleep_doesntTrimMemory() =
+ testScope.runTest {
+ powerInteractor.setAsleepForTest()
+ testScope.runCurrent()
+ verifyZeroInteractions(globalWindowManager)
+ }
+
+ @Test
+ @DisableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
+ fun dozeEnabled_flagDisabled_sleepWithFullDozeAmount_doesntTrimMemory() =
+ testScope.runTest {
+ keyguardRepository.setDreaming(true)
+ keyguardRepository.setDozeAmount(1f)
+ powerInteractor.setAsleepForTest()
+ testScope.runCurrent()
+ verifyZeroInteractions(globalWindowManager)
+ }
+
+ @Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun dozeEnabled_sleepWithFullDozeAmount_trimsMemory() =
testScope.runTest {
keyguardRepository.setDreaming(true)
@@ -105,6 +133,7 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun dozeEnabled_sleepWithoutFullDozeAmount_doesntTrimMemory() =
testScope.runTest {
keyguardRepository.setDreaming(true)
@@ -115,6 +144,7 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun aodEnabled_sleepWithFullDozeAmount_trimsMemoryOnce() {
testScope.runTest {
keyguardRepository.setDreaming(true)
@@ -141,6 +171,7 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun aodEnabled_deviceWakesHalfWayThrough_doesNotTrimMemory() {
testScope.runTest {
keyguardRepository.setDreaming(true)
@@ -172,6 +203,7 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun keyguardTransitionsToGone_trimsFontCache() =
testScope.runTest {
keyguardTransitionRepository.sendTransitionSteps(
@@ -186,6 +218,7 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun keyguardTransitionsToGone_flagDisabled_doesNotTrimFontCache() =
testScope.runTest {
featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, false)
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 a5d577dceecb..abd42387cc2e 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
@@ -1097,6 +1097,41 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
}
@Test
+ fun primaryBouncerToGlanceableHubWhileDreaming() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to PRIMARY_BOUNCER
+ bouncerRepository.setPrimaryShow(true)
+ runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER)
+
+ // GIVEN that we are dreaming and occluded
+ keyguardRepository.setDreaming(true)
+ keyguardRepository.setKeyguardOccluded(true)
+
+ // GIVEN the device is idle on the glanceable hub
+ val idleTransitionState =
+ MutableStateFlow<ObservableCommunalTransitionState>(
+ ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+ )
+ communalInteractor.setTransitionState(idleTransitionState)
+ runCurrent()
+
+ // WHEN the primaryBouncer stops showing
+ bouncerRepository.setPrimaryShow(false)
+ runCurrent()
+
+ // THEN a transition to LOCKSCREEN should occur
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromPrimaryBouncerTransitionInteractor::class.simpleName,
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.GLANCEABLE_HUB,
+ animatorAssertion = { it.isNotNull() },
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun primaryBouncerToDreamingLockscreenHosted() =
testScope.runTest {
// GIVEN device dreaming with the lockscreen hosted dream and not dozing
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/MediaTestUtils.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/MediaTestUtils.kt
index 3437365d9902..4e976d0597cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/MediaTestUtils.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/MediaTestUtils.kt
@@ -17,7 +17,7 @@
package com.android.systemui.media.controls
import com.android.internal.logging.InstanceId
-import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.shared.model.MediaData
class MediaTestUtils {
companion object {
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/domain/pipeline/MediaDataCombineLatestTest.java
index fb101dda6aaf..bb5b57287a9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline;
+package com.android.systemui.media.controls.domain.pipeline;
import static com.google.common.truth.Truth.assertThat;
@@ -33,8 +33,8 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceId;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.models.player.MediaDeviceData;
+import com.android.systemui.media.controls.shared.model.MediaData;
+import com.android.systemui.media.controls.shared.model.MediaDeviceData;
import org.junit.Before;
import org.junit.Rule;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterTest.kt
index 94b9fa4a6582..59eb7bb73de7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.app.smartspace.SmartspaceAction
import android.os.Bundle
@@ -25,10 +25,10 @@ import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_RESUME
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.ui.MediaPlayerData
+import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_RESUME
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.controller.MediaPlayerData
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.settings.UserTracker
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/domain/pipeline/MediaDataManagerTest.kt
index 59d81049ad46..61bfdb548b4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.app.IUriGrantsManager
import android.app.Notification
@@ -51,13 +51,13 @@ import com.android.systemui.InstanceIdSequenceFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_SOURCE
-import com.android.systemui.media.controls.models.recommendation.EXTRA_VALUE_TRIGGER_PERIODIC
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaDataProvider
-import com.android.systemui.media.controls.resume.MediaResumeListener
-import com.android.systemui.media.controls.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.domain.resume.MediaResumeListener
+import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
+import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
@@ -1476,7 +1476,7 @@ class MediaDataManagerTest : SysuiTestCase() {
}
@Test
- fun testOnMediaDataTimedOut_doesNotUpdateLastActiveTime() {
+ fun testOnMediaDataTimedOut_updatesLastActiveTime() {
// GIVEN that the manager has a notification
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
@@ -1487,7 +1487,7 @@ class MediaDataManagerTest : SysuiTestCase() {
val currentTime = clock.elapsedRealtime()
mediaDataManager.setTimedOut(KEY, true, true)
- // THEN the last active time is not changed
+ // THEN the last active time is changed
verify(listener)
.onMediaDataLoaded(
eq(KEY),
@@ -1497,11 +1497,11 @@ class MediaDataManagerTest : SysuiTestCase() {
eq(0),
eq(false)
)
- assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime)
+ assertThat(mediaDataCaptor.value.lastActive).isAtLeast(currentTime)
}
@Test
- fun testOnActiveMediaConverted_doesNotUpdateLastActiveTime() {
+ fun testOnActiveMediaConverted_updatesLastActiveTime() {
// GIVEN that the manager has a notification with a resume action
addNotificationAndLoad()
val data = mediaDataCaptor.value
@@ -1514,6 +1514,42 @@ class MediaDataManagerTest : SysuiTestCase() {
val currentTime = clock.elapsedRealtime()
mediaDataManager.onNotificationRemoved(KEY)
+ // THEN the last active time is changed
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(PACKAGE_NAME),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ assertThat(mediaDataCaptor.value.resumption).isTrue()
+ assertThat(mediaDataCaptor.value.lastActive).isAtLeast(currentTime)
+
+ // Log as a conversion event, not as a new resume control
+ verify(logger).logActiveConvertedToResume(anyInt(), eq(PACKAGE_NAME), eq(instanceId))
+ verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any())
+ }
+
+ @Test
+ fun testOnInactiveMediaConverted_doesNotUpdateLastActiveTime() {
+ // GIVEN that the manager has a notification with a resume action
+ addNotificationAndLoad()
+ val data = mediaDataCaptor.value
+ val instanceId = data.instanceId
+ assertThat(data.resumption).isFalse()
+ mediaDataManager.onMediaDataLoaded(
+ KEY,
+ null,
+ data.copy(resumeAction = Runnable {}, active = false)
+ )
+
+ // WHEN the notification is removed
+ clock.advanceTime(100)
+ val currentTime = clock.elapsedRealtime()
+ mediaDataManager.onNotificationRemoved(KEY)
+
// THEN the last active time is not changed
verify(listener)
.onMediaDataLoaded(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index e3c4c2858b3d..7f3d79f7e288 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.bluetooth.BluetoothLeBroadcast
import android.bluetooth.BluetoothLeBroadcastMetadata
@@ -27,20 +27,25 @@ import android.media.RoutingSessionInfo
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSession
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
+import com.android.settingslib.flags.Flags
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.PhoneMediaDevice
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.util.LocalMediaManagerFactory
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManagerFactory
@@ -82,6 +87,7 @@ private const val NORMAL_APP_NAME = "NORMAL_APP_NAME"
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
public class MediaDeviceManagerTest : SysuiTestCase() {
+ @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
private lateinit var manager: MediaDeviceManager
@Mock private lateinit var controllerFactory: MediaControllerFactory
@@ -667,7 +673,28 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
- fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting() {
+ fun onBroadcastStarted_flagOff_currentMediaDeviceDataIsBroadcasting() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(true)
+ setupBroadcastPackage(BROADCAST_APP_NAME)
+ broadcastCallback.onBroadcastStarted(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isFalse()
+ assertThat(data.enabled).isTrue()
+ assertThat(data.name).isEqualTo(DEVICE_NAME)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStarted_legacy_currentMediaDeviceDataIsBroadcasting() {
val broadcastCallback = setupBroadcastCallback()
setupLeAudioConfiguration(true)
setupBroadcastPackage(BROADCAST_APP_NAME)
@@ -685,7 +712,9 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
- fun onBroadcastStarted_currentMediaDeviceDataIsNotBroadcasting() {
+ @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStarted_legacy_currentMediaDeviceDataIsNotBroadcasting() {
val broadcastCallback = setupBroadcastCallback()
setupLeAudioConfiguration(true)
setupBroadcastPackage(NORMAL_APP_NAME)
@@ -702,6 +731,62 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStopped_legacy_bluetoothLeBroadcastIsDisabledAndBroadcastingButtonIsGone() {
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(false)
+ broadcastCallback.onBroadcastStopped(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isFalse()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting() {
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(true)
+ setupBroadcastPackage(BROADCAST_APP_NAME)
+ broadcastCallback.onBroadcastStarted(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isFalse()
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isEqualTo(context.getString(R.string.audio_sharing_description))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStarted_currentMediaDeviceDataIsNotBroadcasting() {
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(true)
+ setupBroadcastPackage(NORMAL_APP_NAME)
+ broadcastCallback.onBroadcastStarted(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isFalse()
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isEqualTo(context.getString(R.string.audio_sharing_description))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
fun onBroadcastStopped_bluetoothLeBroadcastIsDisabledAndBroadcastingButtonIsGone() {
val broadcastCallback = setupBroadcastCallback()
setupLeAudioConfiguration(false)
@@ -713,6 +798,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
val data = captureDeviceData(KEY)
assertThat(data.showBroadcastButton).isFalse()
+ assertThat(data.name?.equals(context.getString(R.string.audio_sharing_description)))
+ .isFalse()
}
private fun captureCallback(): LocalMediaManager.DeviceCallback {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
index 3099609d42f0..5a3c220b3d23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
@@ -25,7 +25,7 @@ import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
index 8baa06ac0141..3cc65c9524a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.media.MediaMetadata
import android.media.session.MediaController
@@ -24,8 +24,8 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
index 530b86eb4978..55ff231ab6b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume
+package com.android.systemui.media.controls.domain.resume
import android.app.PendingIntent
import android.content.ComponentName
@@ -34,10 +34,10 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.settings.UserTracker
import com.android.systemui.tuner.TunerService
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt
index b45e66bfc31b..8dfa5b8e640c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume
+package com.android.systemui.media.controls.domain.resume
import android.content.ComponentName
import android.content.Context
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/shared/SmartspaceMediaDataTest.kt
index f7c20ac35957..473dc4712c25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/shared/SmartspaceMediaDataTest.kt
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.recommendation
+package com.android.systemui.media.controls.shared
import android.app.smartspace.SmartspaceAction
import android.graphics.drawable.Icon
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.controls.shared.model.NUM_REQUIRED_RECOMMENDATIONS
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
index 32b822d798f8..b509e779a8c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
@@ -20,7 +20,9 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.ui.controller.MediaControlPanel
+import com.android.systemui.media.controls.ui.controller.MediaPlayerData
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/AnimationBindHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt
index 99f56b16ab8b..eb885fd4ae41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/AnimationBindHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.graphics.drawable.Animatable2
import android.graphics.drawable.Drawable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
index a94374680b91..aa297b537c49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.animation.ValueAnimator
import android.graphics.Color
@@ -22,8 +22,8 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.models.GutsViewHolder
-import com.android.systemui.media.controls.models.player.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.GutsViewHolder
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.monet.ColorScheme
import com.android.systemui.surfaceeffects.ripple.MultiRippleController
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MetadataAnimationHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt
index 323b7818ed3d..711669eb2dd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MetadataAnimationHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.animation.Animator
import android.test.suitebuilder.annotation.SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
index 4ec29ceb56d3..8a6b2722b1a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.binder
import android.animation.Animator
import android.animation.ObjectAnimator
@@ -25,7 +25,9 @@ import android.widget.SeekBar
import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.ui.SquigglyProgress
+import com.android.systemui.media.controls.ui.drawable.SquigglyProgress
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
index 50f0eb4eca2c..9f5260c252e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.provider.Settings
import android.test.suitebuilder.annotation.SmallTest
@@ -25,6 +25,8 @@ import android.view.View.VISIBLE
import android.widget.FrameLayout
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index f3b9102da780..f755199b4c72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.app.PendingIntent
import android.content.res.ColorStateList
@@ -38,11 +38,13 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
-import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.ui.MediaHierarchyManager.Companion.LOCATION_QS
+import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaScrollView
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.ActivityStarter
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/controller/MediaControlPanelTest.kt
index 93d612198b44..2e7829d4ea7b 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/controller/MediaControlPanelTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.animation.Animator
import android.animation.AnimatorSet
@@ -69,19 +69,19 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.bluetooth.BroadcastDialogController
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.GutsViewHolder
-import com.android.systemui.media.controls.models.player.MediaAction
-import com.android.systemui.media.controls.models.player.MediaButton
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.player.SeekBarObserver
-import com.android.systemui.media.controls.models.player.SeekBarViewModel
-import com.android.systemui.media.controls.models.recommendation.KEY_SMARTSPACE_APP_NAME
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.KEY_SMARTSPACE_APP_NAME
+import com.android.systemui.media.controls.shared.model.MediaAction
+import com.android.systemui.media.controls.shared.model.MediaButton
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.binder.SeekBarObserver
+import com.android.systemui.media.controls.ui.view.GutsViewHolder
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.media.dialog.MediaOutputDialogFactory
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index 87d093fe2dff..85291b814b3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.graphics.Rect
import android.provider.Settings
@@ -31,7 +31,10 @@ import com.android.systemui.controls.controller.ControlsControllerImplTest.Compa
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.ui.view.MediaCarouselScrollHandler
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
index b701d7f315bc..a73bb2cdf79a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.content.res.Configuration
import android.content.res.Configuration.ORIENTATION_LANDSCAPE
@@ -24,8 +24,9 @@ import android.view.View
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.res.R
import com.android.systemui.util.animation.MeasurementInput
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/SquigglyProgressTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt
index d6cff81c0aaa..0319aaaedd27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/SquigglyProgressTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.drawable
import android.graphics.Canvas
import android.graphics.Color
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
index 74b3fce12790..120836959fdc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.view
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/MediaViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt
index c829d4cbfb71..d3c703ccce46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/MediaViewHolderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.view
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
index e3c8b052327c..e1c2d3f115ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.viewmodel
import android.media.MediaMetadata
import android.media.session.MediaController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index f7873aa3e40e..ca403e0addec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.media.dialog;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -47,6 +48,7 @@ import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.media.LocalMediaManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastSender;
@@ -126,6 +128,13 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
mNotifCollection, mDialogTransitionAnimator,
mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
+
+ // Using a fake package will cause routing operations to fail, so we intercept
+ // scanning-related operations.
+ mMediaOutputController.mLocalMediaManager = mock(LocalMediaManager.class);
+ doNothing().when(mMediaOutputController.mLocalMediaManager).startScan();
+ doNothing().when(mMediaOutputController.mLocalMediaManager).stopScan();
+
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, mBroadcastSender,
mMediaOutputController);
mMediaOutputBaseDialogImpl.onCreate(new Bundle());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
index ce885c0bba2a..a82884377602 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
@@ -25,7 +25,7 @@ import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.util.animation.UniqueObjectHostView;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
index 8a316642a3b0..ff7c970960e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -33,8 +33,8 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.complication.DreamMediaEntryComplication;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.shared.model.MediaData;
import org.junit.Before;
import org.junit.Test;
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 0d1e87433c60..31746a2a46a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -184,6 +184,8 @@ public class NavigationBarTest extends SysuiTestCase {
@Mock
private NavBarButtonClickLogger mNavBarButtonClickLogger;
@Mock
+ private NavbarOrientationTrackingLogger mNavbarOrientationTrackingLogger;
+ @Mock
private ViewTreeObserver mViewTreeObserver;
NavBarHelper mNavBarHelper;
@Mock
@@ -599,7 +601,8 @@ public class NavigationBarTest extends SysuiTestCase {
mWakefulnessLifecycle,
mTaskStackChangeListeners,
new FakeDisplayTracker(mContext),
- mNavBarButtonClickLogger));
+ mNavBarButtonClickLogger,
+ mNavbarOrientationTrackingLogger));
}
private void processAllMessages() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
index 8e72d0636459..e4a4836bcd46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
@@ -58,7 +58,7 @@ import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagsClassic;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSComponent;
import com.android.systemui.qs.external.TileServiceRequestController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index da8d29c622d1..65ede89a1514 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -45,7 +45,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.logging.QSLogger;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 1f7a02962ce2..85d7d9865c7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -10,8 +10,8 @@ import com.android.internal.logging.UiEventLogger
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.media.controls.ui.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.customize.QSCustomizerController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 2db79c2dc527..2c1430844d12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -25,8 +25,8 @@ import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.media.controls.ui.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.customize.QSCustomizerController
import com.android.systemui.qs.logging.QSLogger
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
index 091531e435e4..2f911fffe335 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
@@ -73,6 +73,17 @@ class ScreenshotSoundControllerTest : SysuiTestCase() {
}
@Test
+ fun playCameraSound_illegalStateException_doesNotThrow() = runTest {
+ whenever(mediaPlayer.start()).thenThrow(IllegalStateException())
+
+ val controller = createController()
+ controller.playCameraSound().await()
+
+ verify(mediaPlayer).start()
+ verify(mediaPlayer).release()
+ }
+
+ @Test
fun playCameraSound_soundLoadingSuccessful_mediaPlayerReleases() = runTest {
val controller = createController()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index db455cb31684..950a9dbc2ff3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -115,9 +115,9 @@ import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransition
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -439,8 +439,13 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
)
);
SystemClock systemClock = new FakeSystemClock();
- mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger,
- mInteractionJankMonitor, mJavaAdapter, () -> mShadeInteractor);
+ mStatusBarStateController = new StatusBarStateControllerImpl(
+ mUiEventLogger,
+ mInteractionJankMonitor,
+ mJavaAdapter,
+ () -> mShadeInteractor,
+ () -> mKosmos.getDeviceUnlockedInteractor(),
+ () -> mKosmos.getSceneInteractor());
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
keyguardStatusView.setId(R.id.keyguard_status_view);
@@ -603,9 +608,13 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
new NotificationWakeUpCoordinator(
mDumpManager,
mock(HeadsUpManager.class),
- new StatusBarStateControllerImpl(new UiEventLoggerFake(),
+ new StatusBarStateControllerImpl(
+ new UiEventLoggerFake(),
mInteractionJankMonitor,
- mJavaAdapter, () -> mShadeInteractor),
+ mJavaAdapter,
+ () -> mShadeInteractor,
+ () -> mKosmos.getDeviceUnlockedInteractor(),
+ () -> mKosmos.getSceneInteractor()),
mKeyguardBypassController,
mDozeParameters,
mScreenOffAnimationController,
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 61fee16f0431..3808d309b02b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -180,7 +180,6 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
mTestScope.getBackgroundScope(),
mKosmos.getFakeSceneContainerConfig(),
mKosmos.getSceneDataSource()),
- powerInteractor,
mock(SceneLogger.class),
mKosmos.getDeviceUnlockedInteractor());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 061f88e8a592..0b49a954e848 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -54,8 +54,8 @@ import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransit
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.power.domain.interactor.PowerInteractor;
@@ -208,7 +208,6 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
mTestScope.getBackgroundScope(),
mKosmos.getFakeSceneContainerConfig(),
mKosmos.getSceneDataSource()),
- powerInteractor,
mock(SceneLogger.class),
mKosmos.getDeviceUnlockedInteractor());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index d41842517082..86116a073d9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -15,7 +15,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 1396a430df61..fe16347fa298 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -14,20 +14,30 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.statusbar
import android.animation.ObjectAnimator
+import android.platform.test.annotations.DisableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -38,13 +48,13 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.fromLockscreenTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.fromPrimaryBouncerTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
-import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -56,8 +66,13 @@ import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.testKosmos
+import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -71,8 +86,8 @@ import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -81,7 +96,6 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val testDispatcher = kosmos.testDispatcher
private lateinit var shadeInteractor: ShadeInteractor
private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
private lateinit var fromPrimaryBouncerTransitionInteractor:
@@ -91,7 +105,7 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
private val deviceEntryUdfpsInteractor = mock<DeviceEntryUdfpsInteractor>()
private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>()
- private lateinit var controller: StatusBarStateControllerImpl
+ private lateinit var underTest: StatusBarStateControllerImpl
private lateinit var uiEventLogger: UiEventLoggerFake
@Before
@@ -101,13 +115,15 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
whenever(interactionJankMonitor.end(anyInt())).thenReturn(true)
uiEventLogger = UiEventLoggerFake()
- controller =
+ underTest =
object :
StatusBarStateControllerImpl(
uiEventLogger,
interactionJankMonitor,
- mock(),
- { shadeInteractor }
+ JavaAdapter(testScope.backgroundScope),
+ { shadeInteractor },
+ { kosmos.deviceUnlockedInteractor },
+ { kosmos.sceneInteractor },
) {
override fun createDarkAnimator(): ObjectAnimator {
return mockDarkAnimator
@@ -115,7 +131,7 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
}
val powerInteractor =
- PowerInteractor(FakePowerRepository(), FalsingCollectorFake(), mock(), controller)
+ PowerInteractor(FakePowerRepository(), FalsingCollectorFake(), mock(), underTest)
val keyguardRepository = FakeKeyguardRepository()
val keyguardTransitionRepository = FakeKeyguardTransitionRepository()
val featureFlags = FakeFeatureFlagsClassic()
@@ -168,11 +184,12 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun testChangeState_logged() {
TestableLooper.get(this).runWithLooper {
- controller.state = StatusBarState.KEYGUARD
- controller.state = StatusBarState.SHADE
- controller.state = StatusBarState.SHADE_LOCKED
+ underTest.state = StatusBarState.KEYGUARD
+ underTest.state = StatusBarState.SHADE
+ underTest.state = StatusBarState.SHADE_LOCKED
}
val logs = uiEventLogger.logs
@@ -186,90 +203,199 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
@Test
fun testSetDozeAmountInternal_onlySetsOnce() {
val listener = mock(StatusBarStateController.StateListener::class.java)
- controller.addCallback(listener)
+ underTest.addCallback(listener)
- controller.setAndInstrumentDozeAmount(null, 0.5f, false /* animated */)
- controller.setAndInstrumentDozeAmount(null, 0.5f, false /* animated */)
+ underTest.setAndInstrumentDozeAmount(null, 0.5f, false /* animated */)
+ underTest.setAndInstrumentDozeAmount(null, 0.5f, false /* animated */)
verify(listener).onDozeAmountChanged(eq(0.5f), anyFloat())
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun testSetState_appliesState_sameStateButDifferentUpcomingState() {
- controller.state = StatusBarState.SHADE
- controller.setUpcomingState(StatusBarState.KEYGUARD)
+ underTest.state = StatusBarState.SHADE
+ underTest.setUpcomingState(StatusBarState.KEYGUARD)
- assertEquals(controller.state, StatusBarState.SHADE)
+ assertEquals(underTest.state, StatusBarState.SHADE)
// We should return true (state change was applied) despite going from SHADE to SHADE, since
// the upcoming state was set to KEYGUARD.
- assertTrue(controller.setState(StatusBarState.SHADE))
+ assertTrue(underTest.setState(StatusBarState.SHADE))
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun testSetState_appliesState_differentStateEqualToUpcomingState() {
- controller.state = StatusBarState.SHADE
- controller.setUpcomingState(StatusBarState.KEYGUARD)
+ underTest.state = StatusBarState.SHADE
+ underTest.setUpcomingState(StatusBarState.KEYGUARD)
- assertEquals(controller.state, StatusBarState.SHADE)
+ assertEquals(underTest.state, StatusBarState.SHADE)
// Make sure we apply a SHADE -> KEYGUARD state change when the upcoming state is KEYGUARD.
- assertTrue(controller.setState(StatusBarState.KEYGUARD))
+ assertTrue(underTest.setState(StatusBarState.KEYGUARD))
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun testSetState_doesNotApplyState_currentAndUpcomingStatesSame() {
- controller.state = StatusBarState.SHADE
- controller.setUpcomingState(StatusBarState.SHADE)
+ underTest.state = StatusBarState.SHADE
+ underTest.setUpcomingState(StatusBarState.SHADE)
- assertEquals(controller.state, StatusBarState.SHADE)
+ assertEquals(underTest.state, StatusBarState.SHADE)
// We're going from SHADE -> SHADE, and the upcoming state is also SHADE, this should not do
// anything.
- assertFalse(controller.setState(StatusBarState.SHADE))
+ assertFalse(underTest.setState(StatusBarState.SHADE))
// Double check that we can still force it to happen.
- assertTrue(controller.setState(StatusBarState.SHADE, true /* force */))
+ assertTrue(underTest.setState(StatusBarState.SHADE, true /* force */))
}
@Test
fun testSetDozeAmount_immediatelyChangesDozeAmount_lockscreenTransitionFromAod() {
// Put controller in AOD state
- controller.setAndInstrumentDozeAmount(null, 1f, false)
+ underTest.setAndInstrumentDozeAmount(null, 1f, false)
// When waking from doze, CentralSurfaces#updateDozingState will update the dozing state
// before the doze amount changes
- controller.setIsDozing(false)
+ underTest.setIsDozing(false)
// Animate the doze amount to 0f, as would normally happen
- controller.setAndInstrumentDozeAmount(null, 0f, true)
+ underTest.setAndInstrumentDozeAmount(null, 0f, true)
// Check that the doze amount is immediately set to a value slightly less than 1f. This is
// to ensure that any scrim implementation changes its opacity immediately rather than
// waiting an extra frame. Waiting an extra frame will cause a relayout (which is expensive)
// and cause us to drop a frame during the LOCKSCREEN_TRANSITION_FROM_AOD CUJ.
- assertEquals(0.99f, controller.dozeAmount, 0.009f)
+ assertEquals(0.99f, underTest.dozeAmount, 0.009f)
}
@Test
fun testSetDreamState_invokesCallback() {
val listener = mock(StatusBarStateController.StateListener::class.java)
- controller.addCallback(listener)
+ underTest.addCallback(listener)
- controller.setIsDreaming(true)
+ underTest.setIsDreaming(true)
verify(listener).onDreamingChanged(true)
Mockito.clearInvocations(listener)
- controller.setIsDreaming(false)
+ underTest.setIsDreaming(false)
verify(listener).onDreamingChanged(false)
}
@Test
fun testSetDreamState_getterReturnsCurrentState() {
- controller.setIsDreaming(true)
- assertTrue(controller.isDreaming())
+ underTest.setIsDreaming(true)
+ assertTrue(underTest.isDreaming())
- controller.setIsDreaming(false)
- assertFalse(controller.isDreaming())
+ underTest.setIsDreaming(false)
+ assertFalse(underTest.isDreaming())
}
+
+ @Test
+ @EnableSceneContainer
+ fun start_hydratesStatusBarState_whileLocked() =
+ testScope.runTest {
+ var statusBarState = underTest.state
+ val listener =
+ object : StatusBarStateController.StateListener {
+ override fun onStateChanged(newState: Int) {
+ statusBarState = newState
+ }
+ }
+ underTest.addCallback(listener)
+
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Password
+ )
+ kosmos.fakeDeviceEntryRepository.setUnlocked(false)
+ runCurrent()
+ kosmos.sceneInteractor.changeScene(
+ toScene = SceneKey.Lockscreen,
+ loggingReason = "reason"
+ )
+ runCurrent()
+ assertThat(kosmos.deviceUnlockedInteractor.isDeviceUnlocked.value).isFalse()
+ assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+
+ // Call start to begin hydrating based on the scene framework:
+ underTest.start()
+
+ kosmos.sceneInteractor.changeScene(toScene = SceneKey.Bouncer, loggingReason = "reason")
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
+
+ kosmos.sceneInteractor.changeScene(toScene = SceneKey.Shade, loggingReason = "reason")
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Shade)
+ assertThat(statusBarState).isEqualTo(StatusBarState.SHADE_LOCKED)
+
+ kosmos.sceneInteractor.changeScene(
+ toScene = SceneKey.QuickSettings,
+ loggingReason = "reason"
+ )
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.QuickSettings)
+ assertThat(statusBarState).isEqualTo(StatusBarState.SHADE_LOCKED)
+
+ kosmos.sceneInteractor.changeScene(
+ toScene = SceneKey.Communal,
+ loggingReason = "reason"
+ )
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Communal)
+ assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
+
+ kosmos.sceneInteractor.changeScene(
+ toScene = SceneKey.Lockscreen,
+ loggingReason = "reason"
+ )
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun start_hydratesStatusBarState_whileUnlocked() =
+ testScope.runTest {
+ var statusBarState = underTest.state
+ val listener =
+ object : StatusBarStateController.StateListener {
+ override fun onStateChanged(newState: Int) {
+ statusBarState = newState
+ }
+ }
+ underTest.addCallback(listener)
+
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Password
+ )
+ kosmos.fakeDeviceEntryRepository.setUnlocked(true)
+ runCurrent()
+ kosmos.sceneInteractor.changeScene(toScene = SceneKey.Gone, loggingReason = "reason")
+ runCurrent()
+ assertThat(kosmos.deviceUnlockedInteractor.isDeviceUnlocked.value).isTrue()
+ assertThat(currentScene).isEqualTo(SceneKey.Gone)
+
+ // Call start to begin hydrating based on the scene framework:
+ underTest.start()
+
+ kosmos.sceneInteractor.changeScene(toScene = SceneKey.Shade, loggingReason = "reason")
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Shade)
+ assertThat(statusBarState).isEqualTo(StatusBarState.SHADE)
+
+ kosmos.sceneInteractor.changeScene(
+ toScene = SceneKey.QuickSettings,
+ loggingReason = "reason"
+ )
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.QuickSettings)
+ assertThat(statusBarState).isEqualTo(StatusBarState.SHADE)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 4b145d8b0dd2..5c45b2e53047 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -30,7 +30,7 @@ import android.view.ViewGroup;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index f2ef4e17900d..a4f88fbe1469 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -63,7 +63,7 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index 2b3f9d0f3c39..6fec9ad7bd66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -23,7 +23,7 @@ import android.view.View.VISIBLE
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index f53fc46d8ab2..cd0652e53657 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -20,6 +20,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
@@ -28,7 +29,10 @@ import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.Devic
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.mockito.MockitoAnnotations
@@ -61,6 +65,7 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() {
interactor,
testScope.backgroundScope,
airplaneModeRepository,
+ FakeLogBuffer.Factory.create(),
)
}
@@ -121,8 +126,9 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() {
assertThat(latest).isNull()
}
+ @OptIn(ExperimentalCoroutinesApi::class)
@Test
- fun icon_satelliteIsOff() =
+ fun icon_satelliteIsOn() =
testScope.runTest {
val latest by collectLastValue(underTest.icon)
@@ -133,7 +139,45 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() {
val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
i1.isInService.value = false
- // THEN icon is null because we have service
+ // GIVEN apm is disabled
+ airplaneModeRepository.setIsAirplaneMode(false)
+
+ // Wait for delay to be completed
+ advanceTimeBy(10.seconds)
+
+ // THEN icon is set because we don't have service
+ assertThat(latest).isInstanceOf(Icon::class.java)
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun icon_hysteresisWhenEnablingIcon() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ // GIVEN satellite is allowed
+ repo.isSatelliteAllowedForCurrentLocation.value = true
+
+ // GIVEN all icons are OOS
+ val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
+ i1.isInService.value = false
+
+ // GIVEN apm is disabled
+ airplaneModeRepository.setIsAirplaneMode(false)
+
+ // THEN icon is null because of the hysteresis
+ assertThat(latest).isNull()
+
+ // Wait for delay to be completed
+ advanceTimeBy(10.seconds)
+
+ // THEN icon is set after the delay
assertThat(latest).isInstanceOf(Icon::class.java)
+
+ // GIVEN apm is enabled
+ airplaneModeRepository.setIsAirplaneMode(true)
+
+ // THEN icon is null immediately
+ assertThat(latest).isNull()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 1a3cb87b3422..d2e03861b022 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -61,6 +61,7 @@ import android.widget.ImageButton;
import android.widget.SeekBar;
import androidx.test.core.view.MotionEventBuilder;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.internal.jank.InteractionJankMonitor;
@@ -501,6 +502,7 @@ public class VolumeDialogImplTest extends SysuiTestCase {
}
@Test
+ @FlakyTest(bugId = 326204750)
public void dialogDestroy_removesPostureControllerCallback() {
verify(mPostureController, never()).removeCallback(any());
mDialog.destroy();
@@ -799,8 +801,9 @@ public class VolumeDialogImplTest extends SysuiTestCase {
Log.d(TAG, "teardown: entered");
setOrientation(mOriginalOrientation);
Log.d(TAG, "teardown: after setOrientation");
- mAnimatorTestRule.advanceTimeBy(mLongestHideShowAnimationDuration);
- Log.d(TAG, "teardown: after advanceTimeBy");
+ // Unclear why we used to do this, and it seems to be a source of flakes
+ // mAnimatorTestRule.advanceTimeBy(mLongestHideShowAnimationDuration);
+ Log.d(TAG, "teardown: skipped advanceTimeBy");
mTestableLooper.moveTimeForward(mLongestHideShowAnimationDuration);
Log.d(TAG, "teardown: after moveTimeForward");
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
index 8c5df6efef33..d2387e83e3eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
@@ -12,6 +12,7 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -38,6 +39,7 @@ class WalletContextualLocationsServiceTest : SysuiTestCase() {
private var featureFlags = FakeFeatureFlags()
private lateinit var underTest: WalletContextualLocationsService
private lateinit var testScope: TestScope
+ private lateinit var testDispatcher: CoroutineDispatcher
private var listenerRegisteredCount: Int = 0
private val listener: IWalletCardsUpdatedListener.Stub =
object : IWalletCardsUpdatedListener.Stub() {
@@ -54,8 +56,8 @@ class WalletContextualLocationsServiceTest : SysuiTestCase() {
doNothing().whenever(controller).setSuggestionCardIds(anySet())
if (Looper.myLooper() == null) Looper.prepare()
- val testDispatcher = StandardTestDispatcher()
- testScope = TestScope()
+ testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
featureFlags.set(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS, true)
listenerRegisteredCount = 0
underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index d87df0af6ba9..b25ac24093c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -415,7 +415,6 @@ public class BubblesTest extends SysuiTestCase {
mTestScope.getBackgroundScope(),
mKosmos.getFakeSceneContainerConfig(),
mKosmos.getSceneDataSource()),
- powerInteractor,
mock(SceneLogger.class),
mKosmos.getDeviceUnlockedInteractor());
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
index b8c880b3892f..62a1aa93f35a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
@@ -37,7 +37,7 @@ import com.android.systemui.log.LogBuffer
import com.android.systemui.log.dagger.BiometricLog
import com.android.systemui.log.dagger.BroadcastDispatcherLog
import com.android.systemui.log.dagger.SceneFrameworkLog
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.model.SysUiState
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.DarkIconDispatcher
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/FaceHelpMessageDeferralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/FaceHelpMessageDeferralKosmos.kt
new file mode 100644
index 000000000000..681412117779
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/FaceHelpMessageDeferralKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.domain
+
+import com.android.systemui.biometrics.FaceHelpMessageDeferral
+import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+
+val Kosmos.faceHelpMessageDeferral by Kosmos.Fixture { mock<FaceHelpMessageDeferral>() }
+val Kosmos.faceHelpMessageDeferralFactory by
+ Kosmos.Fixture {
+ val mockFactory = mock<FaceHelpMessageDeferralFactory>()
+ whenever(mockFactory.create()).thenReturn(faceHelpMessageDeferral)
+ mockFactory
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/domain/interactor/FalsingInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/domain/interactor/FalsingInteractorKosmos.kt
index 8fee5b2b305c..43dc372f020f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/domain/interactor/FalsingInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/domain/interactor/FalsingInteractorKosmos.kt
@@ -17,11 +17,13 @@
package com.android.systemui.classifier.domain.interactor
import com.android.systemui.classifier.falsingCollector
+import com.android.systemui.classifier.falsingManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
val Kosmos.falsingInteractor by Fixture {
FalsingInteractor(
collector = falsingCollector,
+ manager = falsingManager,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
index ae7d87783b7c..9d508d23dca7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -38,11 +38,4 @@ class FakeCommunalRepository(
override fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
_transitionState.value = transitionState
}
-
- private val _isCommunalHubShowing: MutableStateFlow<Boolean> = MutableStateFlow(false)
- override val isCommunalHubShowing: Flow<Boolean> = _isCommunalHubShowing
-
- fun setIsCommunalHubShowing(isCommunalHubShowing: Boolean) {
- _isCommunalHubShowing.value = isCommunalHubShowing
- }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 566fc2529954..6af08d3df554 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -29,6 +29,8 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.smartspace.data.repository.smartspaceRepository
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.mock
@@ -47,6 +49,8 @@ val Kosmos.communalInteractor by Fixture {
logBuffer = logcatLogBuffer("CommunalInteractor"),
tableLogBuffer = mock(),
communalSettingsInteractor = communalSettingsInteractor,
+ sceneInteractor = sceneInteractor,
+ sceneContainerFlags = fakeSceneContainerFlags,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
index 00fdceda01d1..23f63e6e20a7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
@@ -16,7 +16,6 @@
package com.android.systemui.communal.domain.interactor
-import com.android.systemui.communal.data.repository.communalRepository
import com.android.systemui.communal.data.repository.communalTutorialRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
@@ -29,7 +28,6 @@ val Kosmos.communalTutorialInteractor by
scope = applicationCoroutineScope,
communalTutorialRepository = communalTutorialRepository,
keyguardInteractor = keyguardInteractor,
- communalRepository = communalRepository,
communalInteractor = communalInteractor,
communalSettingsInteractor = communalSettingsInteractor,
tableLogBuffer = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt
index 77f48db60f41..3ea46872ebcf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt
@@ -30,5 +30,6 @@ val Kosmos.biometricMessageInteractor by
fingerprintPropertyInteractor = fingerprintPropertyInteractor,
faceAuthInteractor = deviceEntryFaceAuthInteractor,
biometricSettingsInteractor = deviceEntryBiometricSettingsInteractor,
+ faceHelpMessageDeferralInteractor = faceHelpMessageDeferralInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractorKosmos.kt
new file mode 100644
index 000000000000..724e943c9f55
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractorKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.biometrics.domain.faceHelpMessageDeferralFactory
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+val Kosmos.faceHelpMessageDeferralInteractor by
+ Kosmos.Fixture {
+ FaceHelpMessageDeferralInteractor(
+ scope = applicationCoroutineScope,
+ faceAuthInteractor = deviceEntryFaceAuthInteractor,
+ biometricSettingsInteractor = deviceEntryBiometricSettingsInteractor,
+ faceHelpMessageDeferralFactory = faceHelpMessageDeferralFactory,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryKosmos.kt
new file mode 100644
index 000000000000..0e439b04f484
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.globalactions.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.globalActionsRepository by Kosmos.Fixture { GlobalActionsRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorKosmos.kt
new file mode 100644
index 000000000000..72143095a114
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.globalactions.domain.interactor
+
+import com.android.systemui.globalactions.data.repository.globalActionsRepository
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.globalActionsInteractor by
+ Kosmos.Fixture { GlobalActionsInteractor(globalActionsRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
index 733340c67e55..460913f75eb4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
@@ -22,11 +22,13 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsIntera
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.aodToLockscreenTransitionViewModel by Fixture {
AodToLockscreenTransitionViewModel(
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+ shadeInteractor = shadeInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index 4939237bbafe..ecf66a297f0d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -24,6 +24,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.dozeParameters
import com.android.systemui.statusbar.phone.screenOffAnimationController
@@ -56,5 +57,6 @@ val Kosmos.keyguardRootViewModel by Fixture {
screenOffAnimationController = screenOffAnimationController,
aodBurnInViewModel = aodBurnInViewModel,
aodAlphaViewModel = aodAlphaViewModel,
+ shadeInteractor = shadeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index f6b32800263a..3fc5af1a50ab 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -30,6 +30,7 @@ import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.globalactions.domain.interactor.globalActionsInteractor
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -91,6 +92,7 @@ class KosmosJavaAdapter(
val fromPrimaryBouncerTransitionInteractor by lazy {
kosmos.fromPrimaryBouncerTransitionInteractor
}
+ val globalActionsInteractor by lazy { kosmos.globalActionsInteractor }
val sceneDataSource by lazy { kosmos.sceneDataSource }
init {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt
index db2cdfa58f8d..7c24b4cc3d60 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
index be559efc7946..7264f7a2bc95 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
@@ -17,8 +17,10 @@
package com.android.systemui.plugins.statusbar
import com.android.internal.logging.uiEventLogger
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.StatusBarStateControllerImpl
import com.android.systemui.util.mockito.mock
@@ -29,7 +31,8 @@ var Kosmos.statusBarStateController by
uiEventLogger,
interactionJankMonitor,
mock(),
- ) {
- shadeInteractor
- }
+ { shadeInteractor },
+ { deviceUnlockedInteractor },
+ { sceneInteractor },
+ )
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerKosmos.kt
new file mode 100644
index 000000000000..960a06940a94
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.privacyDialogController: PrivacyDialogController by
+ Kosmos.Fixture { mock<PrivacyDialogController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerV2Kosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerV2Kosmos.kt
new file mode 100644
index 000000000000..7628c0e64e6a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerV2Kosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.privacyDialogControllerV2: PrivacyDialogControllerV2 by
+ Kosmos.Fixture { mock<PrivacyDialogControllerV2>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeDefaultTilesRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeDefaultTilesRepository.kt
new file mode 100644
index 000000000000..ced29cc9eb47
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeDefaultTilesRepository.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.pipeline.data.repository
+
+import com.android.systemui.qs.pipeline.shared.TileSpec
+
+class FakeDefaultTilesRepository(override val defaultTiles: List<TileSpec> = emptyList()) :
+ DefaultTilesRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
index ae4cf3afe671..a9cce6912f15 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
@@ -23,7 +23,9 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-class FakeTileSpecRepository : TileSpecRepository {
+class FakeTileSpecRepository(
+ private val defaultTilesRepository: DefaultTilesRepository = FakeDefaultTilesRepository()
+) : TileSpecRepository {
private val tilesPerUser = mutableMapOf<Int, MutableStateFlow<List<TileSpec>>>()
@@ -67,4 +69,8 @@ class FakeTileSpecRepository : TileSpecRepository {
value = UserTileSpecRepository.reconcileTiles(value, currentAutoAdded, restoreData)
}
}
+
+ override suspend fun prependDefault(userId: Int) {
+ with(getFlow(userId)) { value = defaultTilesRepository.defaultTiles + value }
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
index 009148266143..604c16fd9e74 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
@@ -18,7 +18,17 @@ package com.android.systemui.qs.pipeline.data.repository
import com.android.systemui.kosmos.Kosmos
-val Kosmos.fakeTileSpecRepository by Kosmos.Fixture { FakeTileSpecRepository() }
+/** This fake uses 0 as the minimum number of tiles. That means that no tiles is a valid state. */
+var Kosmos.fakeMinimumTilesRepository by Kosmos.Fixture { MinimumTilesFixedRepository(0) }
+val Kosmos.minimumTilesRepository: MinimumTilesRepository by
+ Kosmos.Fixture { fakeMinimumTilesRepository }
+
+var Kosmos.fakeDefaultTilesRepository by Kosmos.Fixture { FakeDefaultTilesRepository() }
+val Kosmos.defaultTilesRepository: DefaultTilesRepository by
+ Kosmos.Fixture { fakeDefaultTilesRepository }
+
+val Kosmos.fakeTileSpecRepository by
+ Kosmos.Fixture { FakeTileSpecRepository(defaultTilesRepository) }
var Kosmos.tileSpecRepository: TileSpecRepository by Kosmos.Fixture { fakeTileSpecRepository }
val Kosmos.fakeAutoAddRepository by Kosmos.Fixture { FakeAutoAddRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
index 67df563ec5b0..9ef44c4b9085 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
@@ -24,6 +24,7 @@ import com.android.systemui.qs.external.tileLifecycleManagerFactory
import com.android.systemui.qs.newQSTileFactory
import com.android.systemui.qs.pipeline.data.repository.customTileAddedRepository
import com.android.systemui.qs.pipeline.data.repository.installedTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.minimumTilesRepository
import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
import com.android.systemui.qs.pipeline.shared.logging.qsLogger
import com.android.systemui.qs.pipeline.shared.pipelineFlagsRepository
@@ -37,6 +38,7 @@ val Kosmos.currentTilesInteractor: CurrentTilesInteractor by
tileSpecRepository,
installedTilesRepository,
userRepository,
+ minimumTilesRepository,
customTileStatePersister,
{ newQSTileFactory },
qsTileFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
index fc023758fdf6..ef7aa6308491 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
@@ -19,7 +19,6 @@ package com.android.systemui.scene.domain.interactor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.shared.logger.sceneLogger
@@ -28,7 +27,6 @@ val Kosmos.sceneInteractor by
SceneInteractor(
applicationScope = applicationCoroutineScope,
repository = sceneContainerRepository,
- powerInteractor = powerInteractor,
logger = sceneLogger,
deviceUnlockedInteractor = deviceUnlockedInteractor,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakePrivacyChipRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakePrivacyChipRepository.kt
new file mode 100644
index 000000000000..5bc61e23cd6e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakePrivacyChipRepository.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.privacy.PrivacyItem
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Fake implementation of [PrivacyChipRepository] */
+@SysUISingleton
+class FakePrivacyChipRepository @Inject constructor() : PrivacyChipRepository {
+ private val _isSafetyCenterEnabled = MutableStateFlow(false)
+ override val isSafetyCenterEnabled = _isSafetyCenterEnabled
+
+ private val _privacyItems: MutableStateFlow<List<PrivacyItem>> = MutableStateFlow(emptyList())
+ override val privacyItems = _privacyItems
+
+ private val _isMicCameraIndicationEnabled = MutableStateFlow(false)
+ override val isMicCameraIndicationEnabled = _isMicCameraIndicationEnabled
+
+ private val _isLocationIndicationEnabled = MutableStateFlow(false)
+ override val isLocationIndicationEnabled = _isLocationIndicationEnabled
+
+ fun setIsSafetyCenterEnabled(value: Boolean) {
+ _isSafetyCenterEnabled.value = value
+ }
+
+ fun setPrivacyItems(value: List<PrivacyItem>) {
+ _privacyItems.value = value
+ }
+
+ fun setIsMicCameraIndicationEnabled(value: Boolean) {
+ _isMicCameraIndicationEnabled.value = value
+ }
+
+ fun setIsLocationIndicationEnabled(value: Boolean) {
+ _isLocationIndicationEnabled.value = value
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryKosmos.kt
new file mode 100644
index 000000000000..2428c6156720
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryKosmos.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.shade.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.privacyChipRepository: PrivacyChipRepository by
+ Kosmos.Fixture { fakePrivacyChipRepository }
+val Kosmos.fakePrivacyChipRepository: FakePrivacyChipRepository by
+ Kosmos.Fixture { FakePrivacyChipRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryKosmos.kt
new file mode 100644
index 000000000000..ecc3c9e27208
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.shade.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.policy.nextAlarmController
+
+var Kosmos.shadeHeaderClockRepository: ShadeHeaderClockRepository by
+ Kosmos.Fixture { ShadeHeaderClockRepository(nextAlarmController) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorKosmos.kt
new file mode 100644
index 000000000000..7334286f00c4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.shade.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.privacy.privacyDialogController
+import com.android.systemui.privacy.privacyDialogControllerV2
+import com.android.systemui.shade.data.repository.fakePrivacyChipRepository
+import com.android.systemui.statusbar.policy.deviceProvisionedController
+
+var Kosmos.privacyChipInteractor: PrivacyChipInteractor by
+ Kosmos.Fixture {
+ PrivacyChipInteractor(
+ applicationScope = applicationCoroutineScope,
+ repository = fakePrivacyChipRepository,
+ privacyDialogController = privacyDialogController,
+ privacyDialogControllerV2 = privacyDialogControllerV2,
+ deviceProvisionedController = deviceProvisionedController,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorKosmos.kt
new file mode 100644
index 000000000000..6fd7cf6edbe4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.shade.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.shade.data.repository.shadeHeaderClockRepository
+
+var Kosmos.shadeHeaderClockInteractor: ShadeHeaderClockInteractor by
+ Kosmos.Fixture {
+ ShadeHeaderClockInteractor(
+ repository = shadeHeaderClockRepository,
+ activityStarter = activityStarter,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
index 235da1f5bad9..e4a3896378f6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
@@ -24,7 +24,7 @@ import com.android.systemui.keyguard.domain.interactor.naturalScrollingSettingOb
import com.android.systemui.keyguard.wakefulnessLifecycle
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.media.controls.ui.mediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.mediaHierarchyManager
import com.android.systemui.plugins.activityStarter
import com.android.systemui.qs.ui.adapter.qsSceneAdapter
import com.android.systemui.shade.data.repository.shadeRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorKosmos.kt
new file mode 100644
index 000000000000..0614309a3910
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.domain.interactor
+
+import com.android.settingslib.statusbar.notification.data.repository.FakeNotificationsSoundPolicyRepository
+import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.notificationsSoundPolicyRepository by
+ Kosmos.Fixture { FakeNotificationsSoundPolicyRepository() }
+
+val Kosmos.notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor by
+ Kosmos.Fixture { NotificationsSoundPolicyInteractor(notificationsSoundPolicyRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt
new file mode 100644
index 000000000000..25864aee2136
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.notification.data.repository.HeadsUpNotificationRepository
+import kotlinx.coroutines.flow.MutableStateFlow
+
+val Kosmos.headsUpNotificationRepository by Fixture { FakeHeadsUpNotificationRepository() }
+
+class FakeHeadsUpNotificationRepository : HeadsUpNotificationRepository {
+ override val hasPinnedHeadsUp = MutableStateFlow(false)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HeadsUpNotificationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HeadsUpNotificationInteractorKosmos.kt
new file mode 100644
index 000000000000..d3451075c51b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HeadsUpNotificationInteractorKosmos.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
+import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
+
+val Kosmos.headsUpNotificationInteractor by Fixture {
+ HeadsUpNotificationInteractor(headsUpNotificationRepository)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt
index 0c2b115a8af5..aa2c2a2e1ec3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt
@@ -2,30 +2,45 @@ package com.android.systemui.statusbar.policy
class FakeDeviceProvisionedController : DeviceProvisionedController {
@JvmField var deviceProvisioned = true
+ @JvmField var currentUser = 0
+
+ private val callbacks = mutableSetOf<DeviceProvisionedController.DeviceProvisionedListener>()
+ private val usersSetup = mutableSetOf<Int>()
override fun addCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
- TODO("Not yet implemented")
+ callbacks.add(listener)
}
override fun removeCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
- TODO("Not yet implemented")
+ callbacks.remove(listener)
}
override fun isDeviceProvisioned() = deviceProvisioned
+ @Deprecated("Deprecated in Java")
override fun getCurrentUser(): Int {
- TODO("Not yet implemented")
+ return currentUser
}
override fun isUserSetup(user: Int): Boolean {
- TODO("Not yet implemented")
+ return user in usersSetup
}
override fun isCurrentUserSetup(): Boolean {
- TODO("Not yet implemented")
+ return currentUser in usersSetup
}
override fun isFrpActive(): Boolean {
TODO("Not yet implemented")
}
+
+ fun setCurrentUser(userId: Int) {
+ currentUser = userId
+ callbacks.toSet().forEach { it.onUserSwitched() }
+ }
+
+ fun setUserSetup(userId: Int) {
+ usersSetup.add(userId)
+ callbacks.toSet().forEach { it.onUserSetupChanged() }
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/NextAlarmControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/NextAlarmControllerKosmos.kt
new file mode 100644
index 000000000000..860c3fa016ae
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/NextAlarmControllerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.nextAlarmController: NextAlarmController by
+ Kosmos.Fixture { mock<NextAlarmController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
index fed3e171862d..a3ad2b87d5f5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
@@ -42,6 +42,7 @@ class FakeAudioRepository : AudioRepository {
get() = mutableCommunicationDevice.asStateFlow()
private val models: MutableMap<AudioStream, MutableStateFlow<AudioStreamModel>> = mutableMapOf()
+ private val lastAudibleVolumes: MutableMap<AudioStream, Int> = mutableMapOf()
private fun getAudioStreamModelState(
audioStream: AudioStream
@@ -59,12 +60,9 @@ class FakeAudioRepository : AudioRepository {
)
}
- override suspend fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> =
+ override fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> =
getAudioStreamModelState(audioStream).asStateFlow()
- override suspend fun getCurrentAudioStream(audioStream: AudioStream): AudioStreamModel =
- getAudioStreamModelState(audioStream).value
-
override suspend fun setVolume(audioStream: AudioStream, volume: Int) {
getAudioStreamModelState(audioStream).update { it.copy(volume = volume) }
}
@@ -73,6 +71,9 @@ class FakeAudioRepository : AudioRepository {
getAudioStreamModelState(audioStream).update { it.copy(isMuted = isMuted) }
}
+ override suspend fun getLastAudibleVolume(audioStream: AudioStream): Int =
+ lastAudibleVolumes.getOrDefault(audioStream, 0)
+
fun setMode(newMode: Int) {
mutableMode.value = newMode
}
@@ -88,4 +89,8 @@ class FakeAudioRepository : AudioRepository {
fun setAudioStreamModel(model: AudioStreamModel) {
getAudioStreamModelState(model.audioStream).update { model }
}
+
+ fun setLastAudibleVolume(audioStream: AudioStream, volume: Int) {
+ lastAudibleVolumes[audioStream] = volume
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeLocalMediaRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeLocalMediaRepository.kt
index 7835fc89ea52..284bd55f15d7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeLocalMediaRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeLocalMediaRepository.kt
@@ -27,20 +27,19 @@ class FakeLocalMediaRepository : LocalMediaRepository {
private val volumeBySession: MutableMap<String?, Int> = mutableMapOf()
- private val mutableMediaDevices = MutableStateFlow<Collection<MediaDevice>>(emptyList())
- override val mediaDevices: StateFlow<Collection<MediaDevice>>
+ private val mutableMediaDevices = MutableStateFlow<List<MediaDevice>>(emptyList())
+ override val mediaDevices: StateFlow<List<MediaDevice>>
get() = mutableMediaDevices.asStateFlow()
private val mutableCurrentConnectedDevice = MutableStateFlow<MediaDevice?>(null)
override val currentConnectedDevice: StateFlow<MediaDevice?>
get() = mutableCurrentConnectedDevice.asStateFlow()
- private val mutableRemoteRoutingSessions =
- MutableStateFlow<Collection<RoutingSession>>(emptyList())
- override val remoteRoutingSessions: StateFlow<Collection<RoutingSession>>
+ private val mutableRemoteRoutingSessions = MutableStateFlow<List<RoutingSession>>(emptyList())
+ override val remoteRoutingSessions: StateFlow<List<RoutingSession>>
get() = mutableRemoteRoutingSessions.asStateFlow()
- fun updateMediaDevices(devices: Collection<MediaDevice>) {
+ fun updateMediaDevices(devices: List<MediaDevice>) {
mutableMediaDevices.value = devices
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/FakeSliceFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/FakeSliceFactory.kt
new file mode 100644
index 000000000000..fc406eaae0f1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/FakeSliceFactory.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc
+
+import androidx.slice.Slice
+import androidx.slice.SliceItem
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+
+object FakeSliceFactory {
+
+ fun createSlice(hasError: Boolean, hasSliceItem: Boolean): Slice {
+ return mock {
+ val sliceItem: SliceItem = mock {
+ whenever(format).thenReturn(android.app.slice.SliceItem.FORMAT_SLICE)
+ }
+
+ whenever(items)
+ .thenReturn(
+ buildList {
+ if (hasSliceItem) {
+ add(sliceItem)
+ }
+ }
+ )
+
+ whenever(hints)
+ .thenReturn(
+ buildList {
+ if (hasError) {
+ add(android.app.slice.Slice.HINT_ERROR)
+ }
+ }
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt
new file mode 100644
index 000000000000..f9b7e69eea7d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc
+
+import androidx.slice.SliceViewManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.volume.panel.component.anc.data.repository.FakeAncSliceRepository
+import com.android.systemui.volume.panel.component.anc.domain.interactor.AncSliceInteractor
+
+var Kosmos.sliceViewManager: SliceViewManager by Kosmos.Fixture { mock {} }
+val Kosmos.ancSliceRepository by Kosmos.Fixture { FakeAncSliceRepository() }
+val Kosmos.ancSliceInteractor by
+ Kosmos.Fixture { AncSliceInteractor(ancSliceRepository, testScope.backgroundScope) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
new file mode 100644
index 000000000000..b66d7f974eca
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.anc.data.repository
+
+import androidx.slice.Slice
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeAncSliceRepository : AncSliceRepository {
+
+ private val sliceByWidth = mutableMapOf<Int, MutableStateFlow<Slice?>>()
+
+ override fun ancSlice(width: Int): Flow<Slice?> =
+ sliceByWidth.getOrPut(width) { MutableStateFlow(null) }
+
+ fun putSlice(width: Int, slice: Slice?) {
+ sliceByWidth.getOrPut(width) { MutableStateFlow(null) }.value = slice
+ }
+}
diff --git a/packages/VpnDialogs/res/values-mr/strings.xml b/packages/VpnDialogs/res/values-mr/strings.xml
index cccf3695a537..fc11ce08f8de 100644
--- a/packages/VpnDialogs/res/values-mr/strings.xml
+++ b/packages/VpnDialogs/res/values-mr/strings.xml
@@ -30,7 +30,7 @@
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> हे पूर्ण वेळ कनेक्ट राहण्यासाठी सेट अप केलेले आहे, पण हे आता कनेक्ट होऊ शकत नाही. VPN पुन्हा कनेक्ट होईपर्यंत तुमच्याकडे कनेक्शन नसेल."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN सेटिंग्ज बदला"</string>
- <string name="configure" msgid="4905518375574791375">"कॉंफिगर करा"</string>
+ <string name="configure" msgid="4905518375574791375">"कॉन्फिगर करा"</string>
<string name="disconnect" msgid="971412338304200056">"‍डिस्कनेक्ट करा"</string>
<string name="open_app" msgid="3717639178595958667">"अ‍ॅप उघडा"</string>
<string name="dismiss" msgid="6192859333764711227">"डिसमिस करा"</string>
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index d8a94d8b8b59..e0fe88a1a167 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -1277,6 +1277,20 @@ public class CameraExtensionsProxyService extends Service {
}
@Override
+ public void onCaptureFailed(int captureSequenceId, int reason) {
+ if (Flags.concertMode()) {
+ if (mCaptureCallback != null) {
+ try {
+ mCaptureCallback.onCaptureProcessFailed(captureSequenceId, reason);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture failure due to remote " +
+ "exception!");
+ }
+ }
+ }
+ }
+
+ @Override
public void onCaptureSequenceCompleted(int captureSequenceId) {
if (mCaptureCallback != null) {
try {
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index 4a4c29030f3c..eb3c55cb4ff6 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -255,6 +255,7 @@ android.view.Display
android.view.Display$HdrCapabilities
android.view.Display$Mode
android.view.DisplayInfo
+android.view.inputmethod.InputBinding
android.hardware.SerialManager
android.hardware.SerialManagerInternal
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 015c35eed326..a754ba547767 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -24,6 +24,13 @@ flag {
}
flag {
+ name: "compute_window_changes_on_a11y"
+ namespace: "accessibility"
+ description: "Computes accessibility window changes in accessibility instead of wm package."
+ bug: "322444245"
+}
+
+flag {
name: "deprecate_package_list_observer"
namespace: "accessibility"
description: "Stops using the deprecated PackageListObserver."
@@ -66,6 +73,16 @@ flag {
}
flag {
+ name: "handle_multi_device_input"
+ namespace: "accessibility"
+ description: "Select a single active device when a multi-device stream is received by AccessibilityInputFilter"
+ bug: "310014874"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "pinch_zoom_zero_min_span"
namespace: "accessibility"
description: "Whether to set min span of ScaleGestureDetector to zero."
@@ -80,6 +97,16 @@ flag {
}
flag {
+ name: "reset_hover_event_timer_on_action_up"
+ namespace: "accessibility"
+ description: "Reset the timer for sending hover events on receiving ACTION_UP to guarantee the correct amount of time is available between taps."
+ bug: "326260351"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "scan_packages_without_lock"
namespace: "accessibility"
description: "Scans packages for accessibility service/activity info without holding the A11yMS lock"
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index abcd8e2a2d7e..16119d11ee1e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -25,6 +25,7 @@ import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Region;
import android.os.PowerManager;
+import android.os.SystemClock;
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
@@ -35,6 +36,8 @@ import android.view.InputEvent;
import android.view.InputFilter;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
import android.view.accessibility.AccessibilityEvent;
import com.android.server.LocalServices;
@@ -203,6 +206,62 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private EventStreamState mKeyboardStreamState;
+ /**
+ * The last MotionEvent emitted from the input device that's currently active. This is used to
+ * keep track of which input device is currently active, and also to generate the cancel event
+ * if a new device becomes active.
+ */
+ private MotionEvent mLastActiveDeviceMotionEvent = null;
+
+ private static MotionEvent cancelMotion(MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
+ || event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT
+ || event.getActionMasked() == MotionEvent.ACTION_UP) {
+ throw new IllegalArgumentException("Can't cancel " + event);
+ }
+ final int action;
+ if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER
+ || event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) {
+ action = MotionEvent.ACTION_HOVER_EXIT;
+ } else {
+ action = MotionEvent.ACTION_CANCEL;
+ }
+
+ final int pointerCount;
+ if (event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) {
+ pointerCount = event.getPointerCount() - 1;
+ } else {
+ pointerCount = event.getPointerCount();
+ }
+ final PointerProperties[] properties = new PointerProperties[pointerCount];
+ final PointerCoords[] coords = new PointerCoords[pointerCount];
+ int newPointerIndex = 0;
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ if (event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) {
+ if (event.getActionIndex() == i) {
+ // Skip the pointer that's going away
+ continue;
+ }
+ }
+ final PointerCoords c = new PointerCoords();
+ c.x = event.getX(i);
+ c.y = event.getY(i);
+ coords[newPointerIndex] = c;
+ final PointerProperties p = new PointerProperties();
+ p.id = event.getPointerId(i);
+ p.toolType = event.getToolType(i);
+ properties[newPointerIndex] = p;
+ newPointerIndex++;
+ }
+
+ return MotionEvent.obtain(event.getDownTime(), SystemClock.uptimeMillis(), action,
+ pointerCount, properties, coords,
+ event.getMetaState(), event.getButtonState(),
+ event.getXPrecision(), event.getYPrecision(), event.getDeviceId(),
+ event.getEdgeFlags(), event.getSource(), event.getDisplayId(), event.getFlags(),
+ event.getClassification());
+ }
+
AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
this(context, service, new SparseArray<>(0));
}
@@ -260,6 +319,17 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
AccessibilityTrace.FLAGS_INPUT_FILTER,
"event=" + event + ";policyFlags=" + policyFlags);
}
+ if (Flags.handleMultiDeviceInput()) {
+ if (!shouldProcessMultiDeviceEvent(event, policyFlags)) {
+ // We are only allowing a single device to be active at a time.
+ return;
+ }
+ }
+
+ onInputEventInternal(event, policyFlags);
+ }
+
+ private void onInputEventInternal(InputEvent event, int policyFlags) {
if (mEventHandler.size() == 0) {
if (DEBUG) Slog.d(TAG, "No mEventHandler for event " + event);
super.onInputEvent(event, policyFlags);
@@ -353,6 +423,63 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
}
}
+ boolean shouldProcessMultiDeviceEvent(InputEvent event, int policyFlags) {
+ if (event instanceof MotionEvent motion) {
+ // Only allow 1 device to be sending motion events at a time
+ // If the event is from an active device, let it through.
+ // If the event is not from an active device, only let it through if it starts a new
+ // gesture like ACTION_DOWN or ACTION_HOVER_ENTER
+ final boolean eventIsFromCurrentDevice = mLastActiveDeviceMotionEvent != null
+ && mLastActiveDeviceMotionEvent.getDeviceId() == motion.getDeviceId();
+ final int actionMasked = motion.getActionMasked();
+ switch (actionMasked) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_MOVE: {
+ if (mLastActiveDeviceMotionEvent != null
+ && mLastActiveDeviceMotionEvent.getDeviceId() != motion.getDeviceId()) {
+ // This is a new gesture from a new device. Cancel the existing state
+ // and let this through
+ MotionEvent canceled = cancelMotion(mLastActiveDeviceMotionEvent);
+ onInputEventInternal(canceled, policyFlags);
+ }
+ mLastActiveDeviceMotionEvent = MotionEvent.obtain(motion);
+ return true;
+ }
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_POINTER_UP: {
+ if (eventIsFromCurrentDevice) {
+ mLastActiveDeviceMotionEvent = MotionEvent.obtain(motion);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_HOVER_EXIT: {
+ if (eventIsFromCurrentDevice) {
+ // This is the last event of the gesture from this device.
+ mLastActiveDeviceMotionEvent = null;
+ return true;
+ } else {
+ // Event is from another device
+ return false;
+ }
+ }
+ default: {
+ if (mLastActiveDeviceMotionEvent != null
+ && event.getDeviceId() != mLastActiveDeviceMotionEvent.getDeviceId()) {
+ // This is an event from another device, ignore it.
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
private void processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags) {
if (!state.shouldProcessScroll() && event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
super.onInputEvent(event, policyFlags);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3442a6ac56b3..46db624cf3c1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -64,6 +64,7 @@ import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.app.admin.DevicePolicyManager;
+import android.app.ecm.EnhancedConfirmationManager;
import android.appwidget.AppWidgetManagerInternal;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
@@ -112,6 +113,7 @@ import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
import android.util.ArraySet;
import android.util.IntArray;
+import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -4394,13 +4396,29 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// permittedServices null means all accessibility services are allowed.
boolean allowed = permittedServices == null || permittedServices.contains(packageName);
if (allowed) {
- final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
- final int mode = appOps.noteOpNoThrow(
- AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- uid, packageName, /* attributionTag= */ null, /* message= */ null);
- final boolean ecmEnabled = mContext.getResources().getBoolean(
- R.bool.config_enhancedConfirmationModeEnabled);
- return !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ try {
+ return !mContext.getSystemService(EnhancedConfirmationManager.class)
+ .isRestricted(packageName,
+ AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Exception when retrieving package:" + packageName, e);
+ return false;
+ }
+ } else {
+ try {
+ final int mode = mContext.getSystemService(AppOpsManager.class)
+ .noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+ uid, packageName);
+ final boolean ecmEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
+ return !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
+ } catch (Exception e) {
+ // Fallback in case if app ops is not available in testing.
+ return false;
+ }
+ }
}
return false;
} finally {
@@ -4423,8 +4441,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return true;
}
- RestrictedLockUtils.sendShowRestrictedSettingDialogIntent(mContext,
- packageName, uid);
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ try {
+ Intent settingDialogIntent = mContext
+ .getSystemService(EnhancedConfirmationManager.class)
+ .createRestrictedSettingDialogIntent(packageName,
+ AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
+ mContext.startActivity(settingDialogIntent);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Exception when retrieving package:" + packageName, e);
+ }
+ } else {
+ RestrictedLockUtils.sendShowRestrictedSettingDialogIntent(mContext,
+ packageName, uid);
+ }
return true;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 26c1bc904a1a..b8181505b9c4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -19,6 +19,8 @@ package com.android.server.accessibility;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -27,6 +29,7 @@ import static com.android.server.accessibility.AbstractAccessibilityServiceConne
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Point;
import android.graphics.Region;
import android.os.Binder;
import android.os.Handler;
@@ -37,6 +40,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -52,6 +56,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilitySecurityPolicy.AccessibilityUserManager;
import com.android.server.utils.Slogf;
+import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow;
import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
@@ -60,6 +65,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -440,7 +446,7 @@ public class AccessibilityWindowManager {
updateWindowsByWindowAttributesLocked(windows);
if (DEBUG) {
Slogf.i(LOG_TAG, "mDisplayId=%d, topFocusedDisplayId=%d, currentUserId=%d, "
- + "visibleBgUsers=%s", mDisplayId, topFocusedDisplayId,
+ + "visibleBgUsers=%s", mDisplayId, topFocusedDisplayId,
mAccessibilityUserManager.getCurrentUserIdLocked(),
mAccessibilityUserManager.getVisibleUserIdsLocked());
if (VERBOSE) {
@@ -460,7 +466,7 @@ public class AccessibilityWindowManager {
mTopFocusedWindowToken = topFocusedWindowToken;
if (DEBUG) {
Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): updating windows for "
- + "display %d and token %s",
+ + "display %d and token %s",
topFocusedDisplayId, topFocusedWindowToken);
}
cacheWindows(windows);
@@ -472,12 +478,168 @@ public class AccessibilityWindowManager {
}
else if (DEBUG) {
Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): NOT updating windows for "
- + "display %d and token %s",
+ + "display %d and token %s",
topFocusedDisplayId, topFocusedWindowToken);
}
}
}
+ /**
+ * Called when the windows for accessibility changed. This is called if
+ * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y} is
+ * true.
+ *
+ * @param forceSend Send the windows for accessibility even if they haven't
+ * changed.
+ * @param topFocusedDisplayId The display Id which has the top focused window.
+ * @param topFocusedWindowToken The window token of top focused window.
+ * @param screenSize The size of the display that the change happened.
+ * @param windows The windows for accessibility.
+ */
+ @Override
+ public void onAccessibilityWindowsChanged(boolean forceSend, int topFocusedDisplayId,
+ @NonNull IBinder topFocusedWindowToken, @NonNull Point screenSize,
+ @NonNull List<AccessibilityWindow> windows) {
+ // TODO(b/322444245): Get a screenSize from DisplayManager#getDisplay(int)
+ // .getRealSize().
+ final List<WindowInfo> windowInfoList = createWindowInfoList(screenSize, windows);
+ onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
+ topFocusedWindowToken, windowInfoList);
+ }
+
+ private static List<WindowInfo> createWindowInfoList(@NonNull Point screenSize,
+ @NonNull List<AccessibilityWindow> visibleWindows) {
+ final Set<IBinder> addedWindows = new ArraySet<>();
+ final List<WindowInfo> windows = new ArrayList<>();
+
+ // Avoid allocating Region for each window.
+ final Region regionInWindow = new Region();
+ final Region touchableRegionInScreen = new Region();
+
+ // Iterate until we figure out what is touchable for the entire screen.
+ boolean focusedWindowAdded = false;
+ final Region unaccountedSpace = new Region(0, 0, screenSize.x, screenSize.y);
+ for (final AccessibilityWindow a11yWindow : visibleWindows) {
+ a11yWindow.getTouchableRegionInWindow(regionInWindow);
+ if (windowMattersToAccessibility(a11yWindow, regionInWindow, unaccountedSpace)) {
+ final WindowInfo window = a11yWindow.getWindowInfo();
+ if (window.token != null) {
+ // Even if token is null, the window will be used in calculating visible
+ // windows, but is excluded from the accessibility window list.
+ // TODO(b/322444245): We can call #updateWindowWithWindowAttributes() here.
+ window.regionInScreen.set(regionInWindow);
+ window.layer = addedWindows.size();
+ windows.add(window);
+ addedWindows.add(window.token);
+ }
+
+ if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) {
+ // Account for the space this window takes.
+ a11yWindow.getTouchableRegionInScreen(touchableRegionInScreen);
+ unaccountedSpace.op(touchableRegionInScreen, unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+ }
+
+ focusedWindowAdded |= a11yWindow.isFocused();
+ } else if (a11yWindow.isUntouchableNavigationBar()
+ && a11yWindow.getSystemBarInsetsFrame() != null) {
+ // If this widow is navigation bar without touchable region, accounting the
+ // region of navigation bar inset because all touch events from this region
+ // would be received by launcher, i.e. this region is a un-touchable one
+ // for the application.
+ unaccountedSpace.op(
+ a11yWindow.getSystemBarInsetsFrame(),
+ unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+ }
+
+ if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
+ break;
+ }
+ }
+
+ // Remove child/parent references to windows that were not added.
+ for (final WindowInfo window : windows) {
+ if (!addedWindows.contains(window.parentToken)) {
+ window.parentToken = null;
+ }
+ if (window.childTokens != null) {
+ final int childTokenCount = window.childTokens.size();
+ for (int j = childTokenCount - 1; j >= 0; j--) {
+ if (!addedWindows.contains(window.childTokens.get(j))) {
+ window.childTokens.remove(j);
+ }
+ }
+ // Leave the child token list if empty.
+ }
+ }
+
+ return windows;
+ }
+
+ private static boolean windowMattersToAccessibility(AccessibilityWindow a11yWindow,
+ Region regionInScreen, Region unaccountedSpace) {
+ if (a11yWindow.ignoreRecentsAnimationForAccessibility()) {
+ return false;
+ }
+
+ if (a11yWindow.isFocused()) {
+ return true;
+ }
+
+ // Ignore non-touchable windows, except the split-screen divider, which is
+ // occasionally non-touchable but still useful for identifying split-screen
+ // mode and the PIP menu.
+ if (!a11yWindow.isTouchable()
+ && a11yWindow.getType() != TYPE_DOCK_DIVIDER && !a11yWindow.isPIPMenu()) {
+ return false;
+ }
+
+ // If the window is completely covered by other windows - ignore.
+ if (unaccountedSpace.quickReject(regionInScreen)) {
+ return false;
+ }
+
+ // Add windows of certain types not covered by modal windows.
+ if (isReportedWindowType(a11yWindow.getType())) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static boolean isReportedWindowType(int windowType) {
+ return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
+ && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
+ && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_DRAG
+ && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
+ && windowType != WindowManager.LayoutParams.TYPE_POINTER
+ && windowType != TYPE_MAGNIFICATION_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
+ }
+
+ // Some windows should be excluded from unaccounted space computation, though they still
+ // should be reported
+ private static boolean windowMattersToUnaccountedSpaceComputation(
+ AccessibilityWindow a11yWindow) {
+ // Do not account space of trusted non-touchable windows, except the split-screen
+ // divider.
+ // If it's not trusted, touch events are not sent to the windows behind it.
+ if (!a11yWindow.isTouchable()
+ && (a11yWindow.getType() != TYPE_DOCK_DIVIDER)
+ && a11yWindow.isTrustedOverlay()) {
+ return false;
+ }
+
+ if (a11yWindow.getType() == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
+ return false;
+ }
+ return true;
+ }
+
private void updateWindowsByWindowAttributesLocked(List<WindowInfo> windows) {
for (int i = windows.size() - 1; i >= 0; i--) {
final WindowInfo windowInfo = windows.get(i);
diff --git a/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java b/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java
index 9b27dd347caf..40b6ff01965e 100644
--- a/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java
@@ -232,9 +232,63 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
}
/** Returns true if this descriptor includes usages for the Braille display usage page 0x41. */
- private static boolean isBrailleDisplay(byte[] descriptor) {
- // TODO: b/316036493 - Check that descriptor includes 0x41 reports.
- return true;
+ @VisibleForTesting
+ static boolean isBrailleDisplay(byte[] descriptor) {
+ boolean foundMatch = false;
+ for (int i = 0; i < descriptor.length; i++) {
+ // HID Spec "6.2.2.2 Short Items" defines that the report descriptor is a collection of
+ // items: each item is a collection of bytes where the first byte defines info about
+ // the type of item and the following 0, 1, 2, or 4 bytes are data bytes for that item.
+ // All items in the HID descriptor are expected to be Short Items.
+ final byte itemInfo = descriptor[i];
+ if (!isHidItemShort(itemInfo)) {
+ Slog.w(LOG_TAG, "Item " + itemInfo + " declares unsupported long type");
+ return false;
+ }
+ final int dataSize = getHidItemDataSize(itemInfo);
+ if (i + dataSize >= descriptor.length) {
+ Slog.w(LOG_TAG, "Item " + itemInfo + " specifies size past the remaining bytes");
+ return false;
+ }
+ // The item we're looking for (usage page declaration) should have size 1.
+ if (dataSize == 1) {
+ final byte itemData = descriptor[i + 1];
+ if (isHidItemBrailleDisplayUsagePage(itemInfo, itemData)) {
+ foundMatch = true;
+ }
+ }
+ // Move to the next item by skipping past all data bytes in this item.
+ i += dataSize;
+ }
+ return foundMatch;
+ }
+
+ private static boolean isHidItemShort(byte itemInfo) {
+ // Info bits 7-4 describe the item type, and HID Spec "6.2.2.3 Long Items" says that long
+ // items always have type bits 1111. Otherwise, the item is a short item.
+ return (itemInfo & 0b1111_0000) != 0b1111_0000;
+ }
+
+ private static int getHidItemDataSize(byte itemInfo) {
+ // HID Spec "6.2.2.2 Short Items" says that info bits 0-1 specify the optional data size:
+ // 0, 1, 2, or 4 bytes.
+ return switch (itemInfo & 0b0000_0011) {
+ case 0b00 -> 0;
+ case 0b01 -> 1;
+ case 0b10 -> 2;
+ default -> 4;
+ };
+ }
+
+ private static boolean isHidItemBrailleDisplayUsagePage(byte itemInfo, byte itemData) {
+ // From HID Spec "6.2.2.7 Global Items"
+ final byte usagePageType = 0b0000_0100;
+ // From HID Usage Tables version 1.2.
+ final byte brailleDisplayUsagePage = 0x41;
+ // HID Spec "6.2.2.2 Short Items" says item info bits 2-7 describe the type and
+ // function of the item.
+ final byte itemType = (byte) (itemInfo & 0b1111_1100);
+ return itemType == usagePageType && itemData == brailleDisplayUsagePage;
}
/**
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
index b6223c7b4b35..bf9202f1b266 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
@@ -106,11 +106,30 @@ class EventDispatcher {
return;
}
}
+ final long downTime;
if (action == MotionEvent.ACTION_DOWN) {
- event.setDownTime(event.getEventTime());
+ downTime = event.getEventTime();
} else {
- event.setDownTime(mState.getLastInjectedDownEventTime());
+ downTime = mState.getLastInjectedDownEventTime();
}
+
+ // The only way to change device id of the motion event is by re-creating the whole thing
+ final PointerProperties[] properties = new PointerProperties[event.getPointerCount()];
+ final PointerCoords[] coords = new PointerCoords[event.getPointerCount()];
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ final PointerCoords c = new PointerCoords();
+ event.getPointerCoords(i, c);
+ coords[i] = c;
+ final PointerProperties p = new PointerProperties();
+ event.getPointerProperties(i, p);
+ properties[i] = p;
+ }
+ event = MotionEvent.obtain(downTime, event.getEventTime(), event.getAction(),
+ event.getPointerCount(), properties, coords,
+ event.getMetaState(), event.getButtonState(),
+ event.getXPrecision(), event.getYPrecision(), rawEvent.getDeviceId(),
+ event.getEdgeFlags(), rawEvent.getSource(), event.getDisplayId(), event.getFlags(),
+ event.getClassification());
// If the user is long pressing but the long pressing pointer
// was not exactly over the accessibility focused item we need
// to remap the location of that pointer so the user does not
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 3086ce1ceb40..4fc65bfeca58 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -852,6 +852,11 @@ public class TouchExplorer extends BaseEventStreamTransformation
final int pointerIdBits = (1 << pointerId);
if (mSendHoverEnterAndMoveDelayed.isPending()) {
// If we have not delivered the enter schedule an exit.
+ if (Flags.resetHoverEventTimerOnActionUp()) {
+ // We cancel first to reset the time window so that the user has the full amount of
+ // time to do a multi tap.
+ mSendHoverEnterAndMoveDelayed.repost();
+ }
mSendHoverExitDelayed.post(event, rawEvent, pointerIdBits, policyFlags);
} else {
// The user is touch exploring so we send events for end.
@@ -1554,6 +1559,14 @@ public class TouchExplorer extends BaseEventStreamTransformation
}
}
+ public void repost() {
+ // cancel without clearing
+ if (isPending()) {
+ mHandler.removeCallbacks(this);
+ mHandler.postDelayed(this, mDetermineUserIntentTimeout);
+ }
+ }
+
private boolean isPending() {
return mHandler.hasCallbacks(this);
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 5a3421799aae..c4341b9367ec 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -127,7 +127,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
-import android.os.OutcomeReceiver;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
@@ -2839,15 +2838,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sDebug) {
Slog.d(TAG, "Received GetCredentialResponse from authentication flow");
}
- boolean isCredmanCallbackInvoked = false;
- if (Flags.autofillCredmanIntegration()) {
+ if (Flags.autofillCredmanDevIntegration()) {
GetCredentialResponse response = (GetCredentialResponse) result;
- isCredmanCallbackInvoked = invokeCredentialManagerCallback(response);
- }
-
- if (!isCredmanCallbackInvoked) {
+ sendCredentialManagerResponseToApp(response,
+ /*exception=*/ null, response.getAutofillId());
+ } else if (Flags.autofillCredmanIntegration()) {
Dataset dataset = getDatasetFromCredentialResponse(
- (GetCredentialResponse) result);
+ (GetCredentialResponse) result);
if (dataset != null) {
autoFill(requestId, datasetIdx, dataset, false, UI_TYPE_UNKNOWN);
}
@@ -2892,49 +2889,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- private boolean invokeCredentialManagerCallback(GetCredentialResponse response) {
- synchronized (mLock) {
- return invokeCredentialManagerCallbackLocked(response);
- }
- }
-
- @GuardedBy("mLock")
- private boolean invokeCredentialManagerCallbackLocked(GetCredentialResponse response) {
- AutofillId autofillId = response.getAutofillId();
- if (autofillId != null) {
- OutcomeReceiver<GetCredentialResponse,
- GetCredentialException> callback =
- getCredmanCallbackFromContextsLocked(autofillId);
- if (callback != null) {
- Slog.w(TAG, "Propagating response to Credential Manager callback");
- callback.onResult(response);
- return true;
- } else {
- Slog.w(TAG, "Received Credential Manager response but no callback found");
- }
- } else {
- Slog.w(TAG, "Received Credential Manager response but no autofillId found");
- }
- return false;
- }
-
- @GuardedBy("mLock")
- @Nullable
- private OutcomeReceiver<GetCredentialResponse,
- GetCredentialException> getCredmanCallbackFromContextsLocked(
- @NonNull AutofillId autofillId) {
- final int numContexts = mContexts.size();
- for (int i = numContexts - 1; i >= 0; i--) {
- final FillContext context = mContexts.get(i);
- final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(),
- autofillId);
- if (node != null) {
- return node.getCredentialManagerCallback();
- }
- }
- return null;
- }
-
private Dataset getDatasetFromCredentialResponse(GetCredentialResponse result) {
if (result == null) {
return null;
@@ -5109,10 +5063,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
GetCredentialResponse.class);
- isCredmanCallbackInvoked =
- invokeCredentialManagerCallback(getCredentialResponse);
-
- if (!isCredmanCallbackInvoked) {
+ if (Flags.autofillCredmanDevIntegration()) {
+ sendCredentialManagerResponseToApp(getCredentialResponse,
+ /*exception=*/ null, getCredentialResponse.getAutofillId());
+ } else {
Dataset datasetFromCredential = getDatasetFromCredentialResponse(
getCredentialResponse);
if (datasetFromCredential != null) {
@@ -5128,6 +5082,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.w(TAG, "Credman bottom sheet from pinned "
+ "entry failed with: + " + exception[0] + " , "
+ exception[1]);
+ // TODO(b/326313420): Propagate exception
}
} else {
Slog.d(TAG, "Unknown resultCode from credential "
@@ -6407,6 +6362,37 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ void sendCredentialManagerResponseToApp(@Nullable GetCredentialResponse response,
+ @Nullable GetCredentialException exception, @NonNull AutofillId viewId) {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ Slog.w(TAG, "Call to Session#sendCredentialManagerResponseToApp() rejected "
+ + "- session: " + id + " destroyed");
+ return;
+ }
+ try {
+ final ViewState viewState = mViewStates.get(viewId);
+ if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly()
+ && viewState != null && viewState.id.getSessionId() != id) {
+ if (sVerbose) {
+ Slog.v(TAG, "Skipping sending credential response to view: "
+ + viewId + " as it isn't part of the current session: " + id);
+ }
+ }
+ if (exception != null) {
+ // TODO(b/326313420): Add Exception support
+ } else if (response != null) {
+ mClient.onGetCredentialResponse(id, viewId, response);
+ } else {
+ Slog.w(TAG, "sendCredentialManagerResponseToApp called with null response"
+ + "and exception");
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error sending credential response to activity: " + e);
+ }
+ }
+ }
+
void autoFillApp(Dataset dataset) {
synchronized (mLock) {
if (mDestroyed) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index d580f3a7d7d8..78edb8ead7a0 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -15,7 +15,7 @@
*/
package com.android.server.autofill.ui;
-import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
+import static android.service.autofill.FillResponse.FLAG_CREDENTIAL_MANAGER_RESPONSE;
import static com.android.server.autofill.Helper.paramsToString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sFullScreenMode;
@@ -215,7 +215,7 @@ final class FillUi {
Slog.v(TAG, "overriding maximum visible datasets to " + mVisibleDatasetsMaxCount);
}
} else if (Flags.autofillCredmanIntegration() && (
- (response.getFlags() & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0)) {
+ (response.getFlags() & FLAG_CREDENTIAL_MANAGER_RESPONSE) != 0)) {
mVisibleDatasetsMaxCount = AUTOFILL_CREDMAN_MAX_VISIBLE_DATASETS;
}
else {
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
index 71f2b9e8e10b..e9f959f4b9eb 100644
--- a/services/backup/flags.aconfig
+++ b/services/backup/flags.aconfig
@@ -35,6 +35,15 @@ flag {
}
flag {
+ name: "enable_v_to_u_restore_for_system_components_in_allowlist"
+ namespace: "onboarding"
+ description: "Enables system components to opt in to support restore in V to U downgrade "
+ "scenario without opting in for restoreAnyVersion."
+ bug: "324233962"
+ is_fixed_read_only: true
+}
+
+flag {
name: "enable_increase_datatypes_for_agent_logging"
namespace: "onboarding"
description: "Increase the number of a supported datatypes that an agent can define for its "
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index 9f0deea503cf..6e98e68601f6 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -177,6 +177,10 @@ public class PackageManagerBackupAgent extends BackupAgent {
return mHasMetadata;
}
+ public int getSourceSdk() {
+ return mStoredSdkVersion;
+ }
+
public Metadata getRestoredMetadata(String packageName) {
if (mRestoredSignatures == null) {
Slog.w(TAG, "getRestoredMetadata() before metadata read!");
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index d85dd879e21d..e666442af9c9 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -44,6 +44,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -51,6 +52,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;
@@ -82,6 +84,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -158,6 +161,12 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// When finished call listener
private final OnTaskFinishedListener mListener;
+ // List of packages that support V-> U downgrade but do not have RestoreAnyVersion set to true.
+ private List<String> mVToUAllowlist;
+
+ // List of packages that have RestoreAnyVersion set to true but do not support V-> U downgrade.
+ private List<String> mVToUDenylist;
+
// Key/value: bookkeeping about staged data and files for agent access
private File mBackupDataName;
private File mStageName;
@@ -172,7 +181,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
@VisibleForTesting
PerformUnifiedRestoreTask(
UserBackupManagerService backupManagerService,
- TransportConnection transportConnection) {
+ TransportConnection transportConnection,
+ String vToUAllowlist, String vToUDenyList) {
mListener = null;
mAgentTimeoutParameters = null;
mOperationStorage = null;
@@ -183,6 +193,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
mBackupEligibilityRules = null;
this.backupManagerService = backupManagerService;
mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(/* monitor= */ null);
+ mVToUAllowlist = createVToUList(vToUAllowlist);
+ mVToUDenylist = createVToUList(vToUDenyList);
}
// This task can assume that the wakelock is properly held for it and doesn't have to worry
@@ -223,6 +235,18 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
mBackupEligibilityRules = backupEligibilityRules;
+ mVToUAllowlist =
+ createVToUList(
+ Settings.Secure.getStringForUser(
+ backupManagerService.getContext().getContentResolver(),
+ Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
+ mUserId));
+ mVToUDenylist =
+ createVToUList(
+ Settings.Secure.getStringForUser(
+ backupManagerService.getContext().getContentResolver(),
+ Settings.Secure.V_TO_U_RESTORE_DENYLIST,
+ mUserId));
if (targetPackage != null) {
// Single package restore
@@ -636,60 +660,29 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// Data is from a "newer" version of the app than we have currently
// installed. If the app has not declared that it is prepared to
// handle this case, we do not attempt the restore.
- if ((mCurrentPackage.applicationInfo.flags
- & ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
- == 0) {
- String message =
- "Source version "
- + metaInfo.versionCode
- + " > installed version "
- + mCurrentPackage.getLongVersionCode();
- Slog.w(TAG, "Package " + pkgName + ": " + message);
- Bundle monitoringExtras =
- mBackupManagerMonitorEventSender.putMonitoringExtra(
- null,
- BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
- metaInfo.versionCode);
- monitoringExtras =
- mBackupManagerMonitorEventSender.putMonitoringExtra(
- monitoringExtras,
- BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
- false);
- monitoringExtras = addRestoreOperationTypeToEvent(monitoringExtras);
- mBackupManagerMonitorEventSender.monitorEvent(
- BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
- mCurrentPackage,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- monitoringExtras);
- EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, message);
- nextState = UnifiedRestoreState.RUNNING_QUEUE;
- return;
+ if (mIsSystemRestore
+ && isVToUDowngrade(mPmAgent.getSourceSdk(), android.os.Build.VERSION.SDK_INT)) {
+ if (isPackageEligibleForVToURestore(mCurrentPackage)) {
+ Slog.i(TAG, "Package " + pkgName
+ + " is eligible for V to U downgrade scenario");
+ } else {
+ String message = "Package not eligible for V to U downgrade scenario";
+ Slog.i(TAG, pkgName + " : " + message);
+ EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, message);
+ nextState = UnifiedRestoreState.RUNNING_QUEUE;
+ return;
+ }
} else {
- if (DEBUG) {
- Slog.v(
- TAG,
- "Source version "
- + metaInfo.versionCode
- + " > installed version "
- + mCurrentPackage.getLongVersionCode()
- + " but restoreAnyVersion");
+ if ((mCurrentPackage.applicationInfo.flags
+ & ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
+ == 0) {
+ // Downgrade scenario with RestoreAnyVersion flag off
+ logDowngradeScenario(/* isRestoreAnyVersion */ false, metaInfo);
+ nextState = UnifiedRestoreState.RUNNING_QUEUE;
+ return;
+ } else {
+ logDowngradeScenario(/* isRestoreAnyVersion */ true, metaInfo);
}
- Bundle monitoringExtras =
- mBackupManagerMonitorEventSender.putMonitoringExtra(
- null,
- BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
- metaInfo.versionCode);
- monitoringExtras =
- mBackupManagerMonitorEventSender.putMonitoringExtra(
- monitoringExtras,
- BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
- true);
- monitoringExtras = addRestoreOperationTypeToEvent(monitoringExtras);
- mBackupManagerMonitorEventSender.monitorEvent(
- BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
- mCurrentPackage,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- monitoringExtras);
}
}
@@ -1673,4 +1666,86 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
return mBackupManagerMonitorEventSender.putMonitoringExtra(
extras, BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE, RESTORE);
}
+
+ // checks the sdk of the target/source device for a B&R operation.
+ // system components can opt in/out of V->U restore via allowlists. All other apps are
+ // not impacted
+ @SuppressWarnings("AndroidFrameworkCompatChange")
+ @VisibleForTesting
+ protected boolean isVToUDowngrade(int sourceSdk, int targetSdk) {
+ // We assume that if the source sdk is greater than U then the source is V.
+ return Flags.enableVToURestoreForSystemComponentsInAllowlist()
+ && (sourceSdk > Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ && (targetSdk == Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+ }
+
+ @VisibleForTesting
+ protected List<String> createVToUList(@Nullable String listString) {
+ // The allowlist/denylist is stored as a comma-separated list of package names
+ List<String> list = new ArrayList<>();
+ if (listString != null) {
+ list = Arrays.asList(listString.split(","));
+ }
+ return list;
+ }
+
+ @VisibleForTesting
+ protected boolean isPackageEligibleForVToURestore(PackageInfo mCurrentPackage) {
+ // A package is eligible for V to U downgrade restore if either:
+ // - The package has restoreAnyVersion set to false and is part of the V to U allowlist
+ // (and not in the denylist)
+ // - The package has restoreAnyVersion set to true and is not part of the denylist
+ if (mVToUDenylist.contains(mCurrentPackage.packageName)){
+ return false;
+ } else if ((mCurrentPackage.applicationInfo.flags
+ & ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
+ == 0) {
+ // package has restoreAnyVersion set to false
+ return mVToUAllowlist.contains(mCurrentPackage.packageName);
+ } else {
+ // package has restoreAnyVersion set to true and is nor in denylist
+ return true;
+ }
+ }
+
+ private void logDowngradeScenario(boolean isRestoreAnyVersion, Metadata metaInfo) {
+ Bundle monitoringExtras =
+ mBackupManagerMonitorEventSender.putMonitoringExtra(
+ null,
+ BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
+ metaInfo.versionCode);
+ String message;
+ if (isRestoreAnyVersion) {
+ monitoringExtras =
+ mBackupManagerMonitorEventSender.putMonitoringExtra(
+ monitoringExtras,
+ BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
+ true);
+ message = "Source version "
+ + metaInfo.versionCode
+ + " > installed version "
+ + mCurrentPackage.getLongVersionCode()
+ + " but restoreAnyVersion";
+ } else {
+ monitoringExtras =
+ mBackupManagerMonitorEventSender.putMonitoringExtra(
+ monitoringExtras,
+ BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
+ false);
+ message = "Source version "
+ + metaInfo.versionCode
+ + " > installed version "
+ + mCurrentPackage.getLongVersionCode();
+ EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, mCurrentPackage.packageName,
+ message);
+ }
+ Slog.i(TAG, "Package " + mCurrentPackage.packageName + ": " + message);
+ monitoringExtras = addRestoreOperationTypeToEvent(monitoringExtras);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
+ }
+
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 469209976f5f..7940ca64b330 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -207,6 +207,7 @@ java_library_static {
"notification_flags_lib",
"biometrics_flags_lib",
"am_flags_lib",
+ "com_android_server_accessibility_flags_lib",
"com_android_systemui_shared_flags_lib",
"com_android_wm_shell_flags_lib",
"com.android.server.utils_aconfig-java",
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 81c9ee790d72..4aa5ce19b9b0 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.permission.flags.Flags.ignoreProcessText;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -353,6 +355,13 @@ public abstract class IntentResolver<F, R extends Object> {
public List<R> queryIntentFromList(@NonNull Computer computer, Intent intent,
String resolvedType, boolean defaultOnly, ArrayList<F[]> listCut, int userId,
long customFlags) {
+ if (Intent.ACTION_PROCESS_TEXT.equals(intent.getAction()) && ignoreProcessText()) {
+ // This is for an experiment about deprecating PROCESS_TEXT
+ // Note: SettingsProvider isn't ready early in boot and ACTION_PROCESS_TEXT isn't
+ // queried during boot so we are checking the action before the flag.
+ return Collections.emptyList();
+ }
+
ArrayList<R> resultList = new ArrayList<R>();
final boolean debug = localLOGV ||
@@ -377,6 +386,13 @@ public abstract class IntentResolver<F, R extends Object> {
protected final List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
String resolvedType, boolean defaultOnly, @UserIdInt int userId, long customFlags) {
+ if (Intent.ACTION_PROCESS_TEXT.equals(intent.getAction()) && ignoreProcessText()) {
+ // This is for an experiment about deprecating PROCESS_TEXT
+ // Note: SettingsProvider isn't ready early in boot and ACTION_PROCESS_TEXT isn't
+ // queried during boot so we are checking the action before the flag.
+ return Collections.emptyList();
+ }
+
String scheme = intent.getScheme();
ArrayList<R> finalList = new ArrayList<R>();
diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
index fff283dd41bc..0904c47b61f2 100644
--- a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
+++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
@@ -18,14 +18,17 @@ package com.android.server;
import static android.permission.flags.Flags.sensitiveNotificationAppProtection;
import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS;
-import static com.android.internal.util.Preconditions.checkNotNull;
+import static android.view.flags.Flags.sensitiveContentAppProtection;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManagerInternal;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
+import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
@@ -35,29 +38,30 @@ import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
+import android.view.ISensitiveContentProtectionManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.SensitiveContentPackages.PackageInfo;
import com.android.server.wm.WindowManagerInternal;
+import java.util.Objects;
import java.util.Set;
/**
- * Service that monitors for notifications with sensitive content and protects content from screen
- * sharing
+ * This service protects sensitive content from screen sharing. The service monitors notifications
+ * for sensitive content and protects from screen share. The service also protects sensitive
+ * content rendered on screen during screen share.
*/
public final class SensitiveContentProtectionManagerService extends SystemService {
private static final String TAG = "SensitiveContentProtect";
private static final boolean DEBUG = false;
- private static final boolean sNotificationProtectionEnabled =
- sensitiveNotificationAppProtection();
@VisibleForTesting
@Nullable
NotificationListener mNotificationListener;
- private @Nullable MediaProjectionManager mProjectionManager;
- private @Nullable WindowManagerInternal mWindowManager;
+ @Nullable private MediaProjectionManager mProjectionManager;
+ @Nullable private WindowManagerInternal mWindowManager;
final Object mSensitiveContentProtectionLock = new Object();
@@ -92,7 +96,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
public SensitiveContentProtectionManagerService(@NonNull Context context) {
super(context);
- if (sNotificationProtectionEnabled) {
+ if (sensitiveNotificationAppProtection()) {
mNotificationListener = new NotificationListener();
}
}
@@ -110,14 +114,18 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
init(getContext().getSystemService(MediaProjectionManager.class),
LocalServices.getService(WindowManagerInternal.class));
+ if (sensitiveContentAppProtection()) {
+ publishBinderService(Context.SENSITIVE_CONTENT_PROTECTION_SERVICE,
+ new SensitiveContentProtectionManagerServiceBinder());
+ }
}
@VisibleForTesting
void init(MediaProjectionManager projectionManager, WindowManagerInternal windowManager) {
if (DEBUG) Log.d(TAG, "init");
- checkNotNull(projectionManager, "Failed to get valid MediaProjectionManager");
- checkNotNull(windowManager, "Failed to get valid WindowManagerInternal");
+ Objects.requireNonNull(projectionManager);
+ Objects.requireNonNull(windowManager);
mProjectionManager = projectionManager;
mWindowManager = windowManager;
@@ -126,7 +134,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
// handler, delegate, and binder death recipient
mProjectionManager.addCallback(mProjectionCallback, getContext().getMainThreadHandler());
- if (sNotificationProtectionEnabled) {
+ if (sensitiveNotificationAppProtection()) {
try {
mNotificationListener.registerAsSystemService(
getContext(),
@@ -144,7 +152,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
if (mProjectionManager != null) {
mProjectionManager.removeCallback(mProjectionCallback);
}
- if (sNotificationProtectionEnabled) {
+ if (sensitiveNotificationAppProtection()) {
try {
mNotificationListener.unregisterAsSystemService();
} catch (RemoteException e) {
@@ -169,7 +177,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
synchronized (mSensitiveContentProtectionLock) {
mProjectionActive = true;
- if (sNotificationProtectionEnabled) {
+ if (sensitiveNotificationAppProtection()) {
updateAppsThatShouldBlockScreenCapture();
}
}
@@ -305,4 +313,74 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
}
}
}
+
+ /**
+ * Block projection for a package window when the window is showing sensitive content on
+ * the screen, the projection is unblocked when window no more shows sensitive content.
+ *
+ * @param windowToken window where the content is shown.
+ * @param packageName package name.
+ * @param uid uid of the package.
+ * @param isShowingSensitiveContent whether the window is showing sensitive content.
+ */
+ @VisibleForTesting
+ void setSensitiveContentProtection(IBinder windowToken, String packageName, int uid,
+ boolean isShowingSensitiveContent) {
+ synchronized (mSensitiveContentProtectionLock) {
+ if (!mProjectionActive) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "setSensitiveContentProtection - windowToken=" + windowToken
+ + ", package=" + packageName + ", uid=" + uid
+ + ", isShowingSensitiveContent=" + isShowingSensitiveContent);
+ }
+
+ // The window token distinguish this package from packages added for notifications.
+ PackageInfo packageInfo = new PackageInfo(packageName, uid, windowToken);
+ ArraySet<PackageInfo> packageInfos = new ArraySet<>();
+ packageInfos.add(packageInfo);
+ if (isShowingSensitiveContent) {
+ mWindowManager.addBlockScreenCaptureForApps(packageInfos);
+ } else {
+ mWindowManager.removeBlockScreenCaptureForApps(packageInfos);
+ }
+ }
+ }
+
+ private final class SensitiveContentProtectionManagerServiceBinder
+ extends ISensitiveContentProtectionManager.Stub {
+ private final PackageManagerInternal mPackageManagerInternal;
+
+ SensitiveContentProtectionManagerServiceBinder() {
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ }
+
+ public void setSensitiveContentProtection(IBinder windowToken, String packageName,
+ boolean isShowingSensitiveContent) {
+ Trace.beginSection(
+ "SensitiveContentProtectionManagerService.setSensitiveContentProtection");
+ try {
+ int callingUid = Binder.getCallingUid();
+ verifyCallingPackage(callingUid, packageName);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ SensitiveContentProtectionManagerService.this.setSensitiveContentProtection(
+ windowToken, packageName, callingUid, isShowingSensitiveContent);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ private void verifyCallingPackage(int callingUid, String callingPackage) {
+ if (mPackageManagerInternal.getPackageUid(
+ callingPackage, 0, UserHandle.getUserId(callingUid)) != callingUid) {
+ throw new SecurityException("Specified calling package [" + callingPackage
+ + "] does not match the calling uid " + callingUid);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/LmkdStatsReporter.java b/services/core/java/com/android/server/am/LmkdStatsReporter.java
index 507fd9efaffd..595d16d32e7b 100644
--- a/services/core/java/com/android/server/am/LmkdStatsReporter.java
+++ b/services/core/java/com/android/server/am/LmkdStatsReporter.java
@@ -34,7 +34,6 @@ public final class LmkdStatsReporter {
static final String TAG = TAG_WITH_CLASS_NAME ? "LmkdStatsReporter" : TAG_AM;
public static final int KILL_OCCURRED_MSG_SIZE = 80;
- public static final int STATE_CHANGED_MSG_SIZE = 8;
private static final int PRESSURE_AFTER_KILL = 0;
private static final int NOT_RESPONDING = 1;
@@ -79,16 +78,6 @@ public final class LmkdStatsReporter {
}
}
- /**
- * Processes the LMK_STATE_CHANGED packet
- * Logs the change in LMKD state which is used as start/stop boundaries for logging
- * LMK_KILL_OCCURRED event.
- * Code: LMK_STATE_CHANGED = 54
- */
- public static void logStateChanged(int state) {
- FrameworkStatsLog.write(FrameworkStatsLog.LMK_STATE_CHANGED, state);
- }
-
private static int mapKillReason(int reason) {
switch (reason) {
case PRESSURE_AFTER_KILL:
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 3adea7a929cd..89c89944e92e 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -348,7 +348,7 @@ public final class ProcessList {
// LMK_PROCKILL
// LMK_UPDATE_PROPS
// LMK_KILL_OCCURRED
- // LMK_STATE_CHANGED
+ // LMK_START_MONITORING
static final byte LMK_TARGET = 0;
static final byte LMK_PROCPRIO = 1;
static final byte LMK_PROCREMOVE = 2;
@@ -358,7 +358,6 @@ public final class ProcessList {
static final byte LMK_PROCKILL = 6; // Note: this is an unsolicited command
static final byte LMK_UPDATE_PROPS = 7;
static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event
- static final byte LMK_STATE_CHANGED = 9; // Msg to subscribed clients on state changed
static final byte LMK_START_MONITORING = 9; // Start monitoring if delayed earlier
// Low Memory Killer Daemon command codes.
@@ -965,14 +964,6 @@ public final class ProcessList {
foregroundServices.first,
foregroundServices.second);
return true;
- case LMK_STATE_CHANGED:
- if (receivedLen
- != LmkdStatsReporter.STATE_CHANGED_MSG_SIZE) {
- return false;
- }
- final int state = inputData.readInt();
- LmkdStatsReporter.logStateChanged(state);
- return true;
default:
return false;
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 102a960d35f6..e0c24256f5b1 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -167,8 +167,7 @@ public class AudioDeviceInventory {
ads = findBtDeviceStateForAddress(peerAddress, deviceType);
}
if (ads != null) {
- if (ads.getAudioDeviceCategory() != category
- && category != AUDIO_DEVICE_CATEGORY_UNKNOWN) {
+ if (ads.getAudioDeviceCategory() != category) {
ads.setAudioDeviceCategory(category);
mDeviceBroker.postUpdatedAdiDeviceState(ads);
mDeviceBroker.postPersistAudioDeviceSettings();
@@ -892,7 +891,7 @@ public class AudioDeviceInventory {
if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"A2dp config change ignored (scheduled connection change)")
- .printLog(TAG));
+ .printSlog(EventLogger.Event.ALOGI, TAG));
mmi.set(MediaMetrics.Property.EARLY_RETURN, "A2dp config change ignored")
.record();
return;
@@ -929,7 +928,7 @@ public class AudioDeviceInventory {
"APM handleDeviceConfigChange failed for A2DP device addr="
+ address + " codec="
+ AudioSystem.audioFormatToString(codec))
- .printLog(TAG));
+ .printSlog(EventLogger.Event.ALOGE, TAG));
// force A2DP device disconnection in case of error so that AudioService
// state is consistent with audio policy manager state
@@ -940,7 +939,7 @@ public class AudioDeviceInventory {
"APM handleDeviceConfigChange success for A2DP device addr="
+ address
+ " codec=" + AudioSystem.audioFormatToString(codec))
- .printLog(TAG));
+ .printSlog(EventLogger.Event.ALOGI, TAG));
}
}
}
@@ -1707,10 +1706,13 @@ public class AudioDeviceInventory {
if (res != AudioSystem.AUDIO_STATUS_OK) {
final String reason = "not connecting device 0x" + Integer.toHexString(device)
+ " due to command error " + res;
- Slog.e(TAG, reason);
mmi.set(MediaMetrics.Property.EARLY_RETURN, reason)
.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED)
.record();
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "APM failed to make available device 0x" + Integer.toHexString(device)
+ + "addr=" + address + " error=" + res)
+ .printSlog(EventLogger.Event.ALOGE, TAG));
return false;
}
mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, address));
@@ -1736,7 +1738,8 @@ public class AudioDeviceInventory {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"SCO " + (AudioSystem.isInputDevice(device) ? "source" : "sink")
+ " device addr=" + address
- + (connect ? " now available" : " made unavailable")).printLog(TAG));
+ + (connect ? " now available" : " made unavailable"))
+ .printSlog(EventLogger.Event.ALOGI, TAG));
}
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
} else {
@@ -1992,14 +1995,15 @@ public class AudioDeviceInventory {
// double connection is made.
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "APM failed to make available A2DP device addr=" + address
- + " error=" + res).printLog(TAG));
+ "APM failed to make available A2DP device addr="
+ + Utils.anonymizeBluetoothAddress(address)
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
// TODO: connection failed, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"A2DP sink device addr=" + Utils.anonymizeBluetoothAddress(address)
- + " now available").printLog(TAG));
+ + " now available").printSlog(EventLogger.Event.ALOGI, TAG));
}
// Reset A2DP suspend state each time a new sink is connected
@@ -2240,7 +2244,8 @@ public class AudioDeviceInventory {
// removing A2DP device not currently used by AudioPolicy, log but don't act on it
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
"A2DP device " + Utils.anonymizeBluetoothAddress(address)
- + " made unavailable, was not used")).printLog(TAG));
+ + " made unavailable, was not used"))
+ .printSlog(EventLogger.Event.ALOGI, TAG));
mmi.set(MediaMetrics.Property.EARLY_RETURN,
"A2DP device made unavailable, was not used")
.record();
@@ -2258,13 +2263,13 @@ public class AudioDeviceInventory {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM failed to make unavailable A2DP device addr="
+ Utils.anonymizeBluetoothAddress(address)
- + " error=" + res).printLog(TAG));
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
// TODO: failed to disconnect, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
"A2DP device addr=" + Utils.anonymizeBluetoothAddress(address)
- + " made unavailable")).printLog(TAG));
+ + " made unavailable")).printSlog(EventLogger.Event.ALOGI, TAG));
}
mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
@@ -2297,10 +2302,22 @@ public class AudioDeviceInventory {
@GuardedBy("mDevicesLock")
private void makeA2dpSrcAvailable(String address) {
- mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+ final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
AudioSystem.DEVICE_STATE_AVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
+ if (res != AudioSystem.AUDIO_STATUS_OK) {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "APM failed to make available A2DP source device addr="
+ + Utils.anonymizeBluetoothAddress(address)
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
+ // TODO: connection failed, stop here
+ // TODO: return
+ } else {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "A2DP source device addr=" + Utils.anonymizeBluetoothAddress(address)
+ + " now available").printSlog(EventLogger.Event.ALOGI, TAG));
+ }
mConnectedDevices.put(
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "", address));
@@ -2453,14 +2470,14 @@ public class AudioDeviceInventory {
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM failed to make available LE Audio device addr=" + address
- + " error=" + res).printLog(TAG));
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
// TODO: connection failed, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"LE Audio " + (AudioSystem.isInputDevice(device) ? "source" : "sink")
+ " device addr=" + Utils.anonymizeBluetoothAddress(address)
- + " now available").printLog(TAG));
+ + " now available").printSlog(EventLogger.Event.ALOGI, TAG));
}
// Reset LEA suspend state each time a new sink is connected
mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */);
@@ -2502,13 +2519,13 @@ public class AudioDeviceInventory {
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM failed to make unavailable LE Audio device addr=" + address
- + " error=" + res).printLog(TAG));
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
// TODO: failed to disconnect, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"LE Audio device addr=" + Utils.anonymizeBluetoothAddress(address)
- + " made unavailable").printLog(TAG));
+ + " made unavailable").printSlog(EventLogger.Event.ALOGI, TAG));
}
mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
}
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 5d609bca334c..526264d67318 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -153,7 +153,6 @@ public class AuthenticationStatsCollector {
if (authenticationStats.getTotalAttempts() < MINIMUM_ATTEMPTS) {
return;
}
-
// Don't send notification if FRR below the threshold.
if (authenticationStats.getEnrollmentNotifications() >= MAXIMUM_ENROLLMENT_NOTIFICATIONS
|| authenticationStats.getFrr() < mThreshold) {
@@ -161,6 +160,7 @@ public class AuthenticationStatsCollector {
return;
}
+
authenticationStats.resetData();
final boolean hasEnrolledFace = hasEnrolledFace(userId);
@@ -186,6 +186,28 @@ public class AuthenticationStatsCollector {
}
}
+ /**
+ * This is meant for debug purposes only, this will bypass many checks.
+ * The origination of this call should be from an adb shell command sent from
+ * FaceService.
+ *
+ * adb shell cmd face notification
+ */
+ public void sendFaceReEnrollNotification() {
+ mBiometricNotification.sendFaceEnrollNotification(mContext);
+ }
+
+ /**
+ * This is meant for debug purposes only, this will bypass many checks.
+ * The origination of this call should be from an adb shell command sent from
+ * FingerprintService.
+ *
+ * adb shell cmd fingerprint notification
+ */
+ public void sendFingerprintReEnrollNotification() {
+ mBiometricNotification.sendFpEnrollNotification(mContext);
+ }
+
private void onUserRemoved(final int userId) {
mUserAuthenticationStatsMap.remove(userId);
mAuthenticationStatsPersister.removeFrrStats(userId);
diff --git a/services/core/java/com/android/server/biometrics/BiometricNotificationLogger.java b/services/core/java/com/android/server/biometrics/BiometricNotificationLogger.java
new file mode 100644
index 000000000000..51a2b1ac2dfc
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/BiometricNotificationLogger.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+import com.android.server.biometrics.log.BiometricFrameworkStatsLogger;
+
+/**
+ * A class that logs metric info related to the posting/dismissal of Biometric FRR notifications.
+ */
+public class BiometricNotificationLogger extends NotificationListenerService {
+ private static final String TAG = "FRRNotificationListener";
+ private BiometricFrameworkStatsLogger mLogger;
+
+ BiometricNotificationLogger() {
+ this(BiometricFrameworkStatsLogger.getInstance());
+ }
+
+ @VisibleForTesting
+ BiometricNotificationLogger(BiometricFrameworkStatsLogger logger) {
+ mLogger = logger;
+ }
+
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn, RankingMap map) {
+ if (sbn == null || sbn.getTag() == null) {
+ return;
+ }
+ switch (sbn.getTag()) {
+ case BiometricNotificationUtils.FACE_ENROLL_NOTIFICATION_TAG:
+ case BiometricNotificationUtils.FINGERPRINT_ENROLL_NOTIFICATION_TAG:
+ final int modality =
+ sbn.getTag() == BiometricNotificationUtils.FACE_ENROLL_NOTIFICATION_TAG
+ ? BiometricsProtoEnums.MODALITY_FACE
+ : BiometricsProtoEnums.MODALITY_FINGERPRINT;
+ Slog.d(TAG, "onNotificationPosted, tag=(" + sbn.getTag() + ")");
+ mLogger.logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_SHOWN,
+ modality
+ );
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
+ int reason) {
+ if (sbn == null || sbn.getTag() == null) {
+ return;
+ }
+ switch (sbn.getTag()) {
+ case BiometricNotificationUtils.FACE_ENROLL_NOTIFICATION_TAG:
+ case BiometricNotificationUtils.FINGERPRINT_ENROLL_NOTIFICATION_TAG:
+ Slog.d(TAG, "onNotificationRemoved, tag=("
+ + sbn.getTag() + "), reason=(" + reason + ")");
+ final int modality =
+ sbn.getTag() == BiometricNotificationUtils.FACE_ENROLL_NOTIFICATION_TAG
+ ? BiometricsProtoEnums.MODALITY_FACE
+ : BiometricsProtoEnums.MODALITY_FINGERPRINT;
+ switch (reason) {
+ // REASON_CLICK = 1
+ case NotificationListenerService.REASON_CLICK:
+ mLogger.logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_CLICKED,
+ modality);
+ break;
+ // REASON_CANCEL = 2
+ case NotificationListenerService.REASON_CANCEL:
+ mLogger.logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_DISMISSED,
+ modality);
+ break;
+ default:
+ Slog.d(TAG, "unhandled reason, ignoring logging");
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index fc948da260e6..894b4d5d0b36 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -32,6 +32,7 @@ import android.app.UserSwitchObserver;
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustManager;
import android.content.ContentResolver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
@@ -143,6 +144,8 @@ public class BiometricService extends SystemService {
private final BiometricCameraManager mBiometricCameraManager;
+ private final BiometricNotificationLogger mBiometricNotificationLogger;
+
/**
* Tracks authenticatorId invalidation. For more details, see
* {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}.
@@ -1100,6 +1103,10 @@ public class BiometricService extends SystemService {
return new BiometricCameraManagerImpl(context.getSystemService(CameraManager.class),
context.getSystemService(SensorPrivacyManager.class));
}
+
+ public BiometricNotificationLogger getNotificationLogger() {
+ return new BiometricNotificationLogger();
+ }
}
/**
@@ -1133,6 +1140,7 @@ public class BiometricService extends SystemService {
mBiometricCameraManager = injector.getBiometricCameraManager(context);
mKeystoreAuthorization = injector.getKeystoreAuthorizationService();
mGateKeeper = injector.getGateKeeperService();
+ mBiometricNotificationLogger = injector.getNotificationLogger();
try {
injector.getActivityManagerService().registerUserSwitchObserver(
@@ -1157,6 +1165,20 @@ public class BiometricService extends SystemService {
mInjector.publishBinderService(this, mImpl);
mBiometricStrengthController = mInjector.getBiometricStrengthController(this);
mBiometricStrengthController.startListening();
+
+ mHandler.post(new Runnable(){
+ @Override
+ public void run() {
+ try {
+ mBiometricNotificationLogger.registerAsSystemService(getContext(),
+ new ComponentName(getContext(), BiometricNotificationLogger.class),
+ UserHandle.USER_ALL);
+ } catch (RemoteException e) {
+ // Intra-process call, should never happen.
+ }
+ }
+
+ });
}
private boolean isStrongBiometric(int id) {
@@ -1508,4 +1530,5 @@ public class BiometricService extends SystemService {
pw.println("CurrentSession: " + mAuthSession);
pw.println();
}
+
}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
index 6bd48802baf5..f31b2e11b021 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
@@ -113,14 +113,16 @@ public class BiometricFrameworkStatsLogger {
/** {@see FrameworkStatsLog.BIOMETRIC_ENROLLED}. */
public void enroll(int statsModality, int statsAction, int statsClient,
- int targetUserId, long latency, boolean enrollSuccessful, float ambientLightLux) {
+ int targetUserId, long latency, boolean enrollSuccessful, float ambientLightLux,
+ int source) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
statsModality,
targetUserId,
sanitizeLatency(latency),
enrollSuccessful,
-1, /* sensorId */
- ambientLightLux);
+ ambientLightLux,
+ source);
}
/** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */
@@ -239,6 +241,12 @@ public class BiometricFrameworkStatsLogger {
-1 /* sensorId */);
}
+ /** {@see FrameworkStatsLog.BIOMETRIC_FRR_NOTIFICATION}. */
+ public void logFrameworkNotification(int action, int modality) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_FRR_NOTIFICATION,
+ action, modality);
+ }
+
private long sanitizeLatency(long latency) {
if (latency < 0) {
Slog.w(TAG, "found a negative latency : " + latency);
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
index dbef7178efd0..cd5d0c8dc3f2 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
@@ -252,7 +252,8 @@ public class BiometricLogger {
}
/** Log enrollment outcome. */
- public void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful) {
+ public void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful,
+ int source) {
if (!mShouldLogMetrics) {
return;
}
@@ -273,7 +274,7 @@ public class BiometricLogger {
}
mSink.enroll(mStatsModality, mStatsAction, mStatsClient,
- targetUserId, latency, enrollSuccessful, mALSProbe.getMostRecentLux());
+ targetUserId, latency, enrollSuccessful, mALSProbe.getMostRecentLux(), source);
}
/** Report unexpected enrollment reported by the HAL. */
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
index 2aec9aefa8d1..0e22f7511af9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
@@ -23,6 +23,9 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.face.FaceEnrollOptions;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
@@ -36,25 +39,28 @@ public class BiometricNotificationUtils {
private static final String TAG = "BiometricNotificationUtils";
private static final String FACE_RE_ENROLL_NOTIFICATION_TAG = "FaceReEnroll";
- private static final String FACE_ENROLL_NOTIFICATION_TAG = "FaceEnroll";
- private static final String FINGERPRINT_ENROLL_NOTIFICATION_TAG = "FingerprintEnroll";
private static final String BAD_CALIBRATION_NOTIFICATION_TAG = "FingerprintBadCalibration";
private static final String KEY_RE_ENROLL_FACE = "re_enroll_face_unlock";
private static final String FACE_SETTINGS_ACTION = "android.settings.FACE_SETTINGS";
- private static final String FINGERPRINT_SETTINGS_ACTION =
- "android.settings.FINGERPRINT_SETTINGS";
private static final String FACE_ENROLL_ACTION = "android.settings.FACE_ENROLL";
private static final String FINGERPRINT_ENROLL_ACTION = "android.settings.FINGERPRINT_ENROLL";
+ private static final String FINGERPRINT_SETTINGS_ACTION =
+ "android.settings.FINGERPRINT_SETTINGS";
private static final String SETTINGS_PACKAGE = "com.android.settings";
private static final String FACE_ENROLL_CHANNEL = "FaceEnrollNotificationChannel";
private static final String FACE_RE_ENROLL_CHANNEL = "FaceReEnrollNotificationChannel";
private static final String FINGERPRINT_ENROLL_CHANNEL = "FingerprintEnrollNotificationChannel";
private static final String FINGERPRINT_BAD_CALIBRATION_CHANNEL =
"FingerprintBadCalibrationNotificationChannel";
- private static final int NOTIFICATION_ID = 1;
private static final long NOTIFICATION_INTERVAL_MS = 24 * 60 * 60 * 1000;
private static long sLastAlertTime = 0;
+ private static final String ACTION_BIOMETRIC_FRR_DISMISS = "action_biometric_frr_dismiss";
+ // Dismissal action for FRR notification.
+ private static final Intent DISMISS_FRR_INTENT = new Intent(ACTION_BIOMETRIC_FRR_DISMISS);
+ public static final int NOTIFICATION_ID = 1;
+ public static final String FACE_ENROLL_NOTIFICATION_TAG = "FaceEnroll";
+ public static final String FINGERPRINT_ENROLL_NOTIFICATION_TAG = "FingerprintEnroll";
/**
* Shows a face re-enrollment notification.
*/
@@ -71,7 +77,6 @@ public class BiometricNotificationUtils {
final Intent intent = new Intent(FACE_SETTINGS_ACTION);
intent.setPackage(SETTINGS_PACKAGE);
- intent.putExtra(KEY_RE_ENROLL_FACE, true);
final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
@@ -79,13 +84,14 @@ public class BiometricNotificationUtils {
showNotificationHelper(context, name, title, content, pendingIntent, FACE_RE_ENROLL_CHANNEL,
Notification.CATEGORY_SYSTEM, FACE_RE_ENROLL_NOTIFICATION_TAG,
- Notification.VISIBILITY_SECRET);
+ Notification.VISIBILITY_SECRET, false);
}
/**
* Shows a face enrollment notification.
*/
public static void showFaceEnrollNotification(@NonNull Context context) {
+ Slog.d(TAG, "Showing Face Enroll Notification");
final String name =
context.getString(R.string.device_unlock_notification_name);
@@ -96,6 +102,8 @@ public class BiometricNotificationUtils {
final Intent intent = new Intent(FACE_ENROLL_ACTION);
intent.setPackage(SETTINGS_PACKAGE);
+ intent.putExtra(BiometricManager.EXTRA_ENROLL_REASON,
+ FaceEnrollOptions.ENROLL_REASON_RE_ENROLL_NOTIFICATION);
final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
@@ -103,14 +111,15 @@ public class BiometricNotificationUtils {
showNotificationHelper(context, name, title, content, pendingIntent, FACE_ENROLL_CHANNEL,
Notification.CATEGORY_RECOMMENDATION, FACE_ENROLL_NOTIFICATION_TAG,
- Notification.VISIBILITY_PUBLIC);
+ Notification.VISIBILITY_PUBLIC, true);
+
}
/**
* Shows a fingerprint enrollment notification.
*/
public static void showFingerprintEnrollNotification(@NonNull Context context) {
-
+ Slog.d(TAG, "Showing Fingerprint Enroll Notification");
final String name =
context.getString(R.string.device_unlock_notification_name);
final String title =
@@ -120,6 +129,8 @@ public class BiometricNotificationUtils {
final Intent intent = new Intent(FINGERPRINT_ENROLL_ACTION);
intent.setPackage(SETTINGS_PACKAGE);
+ intent.putExtra(BiometricManager.EXTRA_ENROLL_REASON,
+ FingerprintEnrollOptions.ENROLL_REASON_RE_ENROLL_NOTIFICATION);
final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
@@ -127,7 +138,8 @@ public class BiometricNotificationUtils {
showNotificationHelper(context, name, title, content, pendingIntent,
Notification.CATEGORY_RECOMMENDATION, FINGERPRINT_ENROLL_CHANNEL,
- FINGERPRINT_ENROLL_NOTIFICATION_TAG, Notification.VISIBILITY_PUBLIC);
+ FINGERPRINT_ENROLL_NOTIFICATION_TAG, Notification.VISIBILITY_PUBLIC, true);
+
}
/**
@@ -162,17 +174,22 @@ public class BiometricNotificationUtils {
showNotificationHelper(context, name, title, content, pendingIntent,
Notification.CATEGORY_SYSTEM, FINGERPRINT_BAD_CALIBRATION_CHANNEL,
- BAD_CALIBRATION_NOTIFICATION_TAG, Notification.VISIBILITY_SECRET);
+ BAD_CALIBRATION_NOTIFICATION_TAG, Notification.VISIBILITY_SECRET, false);
}
private static void showNotificationHelper(Context context, String name, String title,
String content, PendingIntent pendingIntent, String category,
- String channelName, String notificationTag, int visibility) {
+ String channelName, String notificationTag, int visibility,
+ boolean listenToDismissEvent) {
+ Slog.v(TAG," listenToDismissEvent = " + listenToDismissEvent);
+ final PendingIntent dismissIntent = PendingIntent.getActivityAsUser(context,
+ 0 /* requestCode */, DISMISS_FRR_INTENT, PendingIntent.FLAG_IMMUTABLE /* flags */,
+ null /* options */, UserHandle.CURRENT);
final NotificationManager notificationManager =
context.getSystemService(NotificationManager.class);
final NotificationChannel channel = new NotificationChannel(channelName, name,
NotificationManager.IMPORTANCE_HIGH);
- final Notification notification = new Notification.Builder(context, channelName)
+ final Notification.Builder builder = new Notification.Builder(context, channelName)
.setSmallIcon(R.drawable.ic_lock)
.setContentTitle(title)
.setContentText(content)
@@ -183,12 +200,17 @@ public class BiometricNotificationUtils {
.setAutoCancel(true)
.setCategory(category)
.setContentIntent(pendingIntent)
- .setVisibility(visibility)
- .build();
+ .setVisibility(visibility);
+
+ if (listenToDismissEvent) {
+ builder.setDeleteIntent(dismissIntent);
+ }
+ final Notification notification = builder.build();
notificationManager.createNotificationChannel(channel);
notificationManager.notifyAsUser(notificationTag, NOTIFICATION_ID, notification,
UserHandle.CURRENT);
+
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 8e7004d8fde5..af6de5c7316a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -45,6 +45,7 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
private long mEnrollmentStartTimeMs;
private final boolean mHasEnrollmentsBeforeStarting;
+ private final int mEnrollReason;
/**
* @return true if the user has already enrolled the maximum number of templates.
@@ -55,13 +56,15 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
int timeoutSec, int sensorId, boolean shouldVibrate,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ int enrollReason) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
shouldVibrate, logger, biometricContext);
mBiometricUtils = utils;
mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length);
mTimeoutSec = timeoutSec;
mHasEnrollmentsBeforeStarting = hasEnrollments();
+ mEnrollReason = enrollReason;
}
@Override
@@ -91,7 +94,7 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
getLogger().logOnEnrolled(getTargetUserId(),
System.currentTimeMillis() - mEnrollmentStartTimeMs,
- true /* enrollSuccessful */);
+ true /* enrollSuccessful */, mEnrollReason);
mCallback.onClientFinished(this, true /* success */);
}
notifyUserActivity();
@@ -119,7 +122,7 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
public void onError(int error, int vendorCode) {
getLogger().logOnEnrolled(getTargetUserId(),
System.currentTimeMillis() - mEnrollmentStartTimeMs,
- false /* enrollSuccessful */);
+ false /* enrollSuccessful */, mEnrollReason);
super.onError(error, vendorCode);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 321e951ec09b..68b4e3fb51ba 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -37,6 +37,7 @@ import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceSensorConfigurations;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.FaceServiceReceiver;
@@ -44,6 +45,7 @@ import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.face.IFaceService;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.NativeHandle;
import android.os.RemoteException;
@@ -210,7 +212,8 @@ public class FaceService extends SystemService {
@Override // Binder call
public long enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
- final int[] disabledFeatures, Surface previewSurface, boolean debugConsent) {
+ final int[] disabledFeatures, Surface previewSurface, boolean debugConsent,
+ FaceEnrollOptions options) {
super.enroll_enforcePermission();
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
@@ -220,7 +223,8 @@ public class FaceService extends SystemService {
}
return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
- receiver, opPackageName, disabledFeatures, previewSurface, debugConsent);
+ receiver, opPackageName, disabledFeatures, previewSurface, debugConsent,
+ options);
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@@ -958,4 +962,25 @@ public class FaceService extends SystemService {
}
}
}
+
+ /**
+ * This should only be called from FaceShellCommand class.
+ */
+ void sendFaceReEnrollNotification() {
+ Utils.checkPermissionOrShell(getContext(), MANAGE_FACE);
+ if (Build.IS_DEBUGGABLE) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
+ if (provider != null) {
+ FaceProvider faceProvider = (FaceProvider) provider.second;
+ faceProvider.sendFaceReEnrollNotification();
+ } else {
+ Slog.w(TAG, "Null provider for notification");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceShellCommand.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceShellCommand.java
index 187575dcd664..e167bbafc02e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceShellCommand.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceShellCommand.java
@@ -42,6 +42,8 @@ public class FaceShellCommand extends ShellCommand {
return doHelp();
case "sync":
return doSync();
+ case "notification":
+ return doNotify();
default:
getOutPrintWriter().println("Unrecognized command: " + cmd);
}
@@ -59,6 +61,8 @@ public class FaceShellCommand extends ShellCommand {
pw.println(" Print this help text.");
pw.println(" sync");
pw.println(" Sync enrollments now (virtualized sensors only).");
+ pw.println(" notification");
+ pw.println(" Sends a Face re-enrollment notification");
}
private int doHelp() {
@@ -70,4 +74,9 @@ public class FaceShellCommand extends ShellCommand {
mService.syncEnrollmentsNow();
return 0;
}
+
+ private int doNotify() {
+ mService.sendFaceReEnrollNotification();
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index 2cf64b72d01f..6f76cdae4539 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -23,6 +23,7 @@ import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
@@ -79,7 +80,7 @@ public interface ServiceProvider extends BiometricServiceProvider<FaceSensorProp
long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName,
@NonNull int[] disabledFeatures, @Nullable Surface previewSurface,
- boolean debugConsent);
+ boolean debugConsent, FaceEnrollOptions options);
void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index d11f0991a5d4..0fdd57d64d8d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -26,6 +26,7 @@ import android.hardware.biometrics.face.EnrollmentFrame;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticationFrame;
import android.hardware.face.FaceEnrollFrame;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
import android.os.RemoteException;
@@ -155,7 +156,8 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
mContext.getOpPackageName(), new int[0] /* disabledFeatures */,
- null /* previewSurface */, false /* debugConsent */);
+ null /* previewSurface */, false /* debugConsent */,
+ (new FaceEnrollOptions.Builder()).build());
}
@android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 5f370f23134c..781e3f491ec8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -93,9 +93,11 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
@Nullable Surface previewSurface, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- int maxTemplatesPerUser, boolean debugConsent) {
+ int maxTemplatesPerUser, boolean debugConsent,
+ android.hardware.face.FaceEnrollOptions options) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils,
- timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext);
+ timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext,
+ BiometricFaceConstants.reasonToMetric(options.getEnrollReason()));
setRequestId(requestId);
mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist);
@@ -105,6 +107,9 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
mDebugConsent = debugConsent;
mDisabledFeatures = disabledFeatures;
mPreviewSurface = previewSurface;
+ Slog.w(TAG, "EnrollOptions "
+ + android.hardware.face.FaceEnrollOptions.enrollReasonToString(
+ options.getEnrollReason()));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index f469f6224e4c..007b7462f637 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -35,6 +35,7 @@ import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
@@ -519,7 +520,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
- @Nullable Surface previewSurface, boolean debugConsent) {
+ @Nullable Surface previewSurface, boolean debugConsent, FaceEnrollOptions options) {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
@@ -533,7 +534,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
- mBiometricContext, maxTemplatesPerUser, debugConsent);
+ mBiometricContext, maxTemplatesPerUser, debugConsent, options);
if (Flags.deHidl()) {
scheduleForSensor(sensorId, client, mBiometricStateCallback);
} else {
@@ -903,4 +904,11 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
public boolean getTestHalEnabled() {
return mTestHalEnabled;
}
+
+ /**
+ * Sends a face re enroll notification.
+ */
+ public void sendFaceReEnrollNotification() {
+ mAuthenticationStatsCollector.sendFaceReEnrollNotification();
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
index 151ffaa1cb28..0e2367a599a5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
@@ -23,6 +23,7 @@ import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticationFrame;
import android.hardware.face.FaceEnrollFrame;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
import android.os.RemoteException;
@@ -143,7 +144,8 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
mFace10.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
mContext.getOpPackageName(), new int[0] /* disabledFeatures */,
- null /* previewSurface */, false /* debugConsent */);
+ null /* previewSurface */, false /* debugConsent */,
+ (new FaceEnrollOptions.Builder()).build());
}
@android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 48a676ce4937..306ddfa1e083 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -32,6 +32,7 @@ import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
@@ -711,16 +712,17 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
- @Nullable Surface previewSurface, boolean debugConsent) {
+ @Nullable Surface previewSurface, boolean debugConsent,
+ @NonNull FaceEnrollOptions options) {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
if (Flags.deHidl()) {
scheduleEnrollAidl(token, hardwareAuthToken, userId, receiver,
- opPackageName, disabledFeatures, previewSurface, id);
+ opPackageName, disabledFeatures, previewSurface, id, options);
} else {
scheduleEnrollHidl(token, hardwareAuthToken, userId, receiver,
- opPackageName, disabledFeatures, previewSurface, id);
+ opPackageName, disabledFeatures, previewSurface, id, options);
}
});
return id;
@@ -729,7 +731,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
private void scheduleEnrollAidl(@NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
- @Nullable Surface previewSurface, long id) {
+ @Nullable Surface previewSurface, long id,
+ @NonNull FaceEnrollOptions options) {
final com.android.server.biometrics.sensors.face.aidl.FaceEnrollClient client =
new com.android.server.biometrics.sensors.face.aidl.FaceEnrollClient(
mContext, this::getSession, token,
@@ -742,7 +745,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
mAuthenticationStatsCollector), mBiometricContext,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_faceMaxTemplatesPerUser),
- false);
+ false, options);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -770,14 +773,14 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
private void scheduleEnrollHidl(@NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
- @Nullable Surface previewSurface, long id) {
+ @Nullable Surface previewSurface, long id, FaceEnrollOptions options) {
final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, mSensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext);
+ mBiometricContext, options);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
index 27b9c79516af..815cf9180130 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
@@ -23,6 +23,7 @@ import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.Status;
import android.hardware.face.Face;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceManager;
import android.os.IBinder;
import android.os.RemoteException;
@@ -61,15 +62,20 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
@NonNull byte[] hardwareAuthToken, @NonNull String owner, long requestId,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
@Nullable Surface previewSurface, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull FaceEnrollOptions options) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
- timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext);
+ timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext,
+ BiometricFaceConstants.reasonToMetric(options.getEnrollReason()));
setRequestId(requestId);
mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist);
mEnrollIgnoreListVendor = getContext().getResources()
.getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist);
+
+ Slog.w(TAG, "EnrollOptions "
+ + FaceEnrollOptions.enrollReasonToString(options.getEnrollReason()));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index e01d672d7e34..1ba12134ab29 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -48,6 +48,7 @@ import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorConfigurations;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -239,7 +240,8 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public long enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
final int userId, final IFingerprintServiceReceiver receiver,
- final String opPackageName, @FingerprintManager.EnrollReason int enrollReason) {
+ final String opPackageName, @FingerprintManager.EnrollReason int enrollReason,
+ FingerprintEnrollOptions options) {
super.enroll_enforcePermission();
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
@@ -249,7 +251,7 @@ public class FingerprintService extends SystemService {
}
return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
- receiver, opPackageName, enrollReason);
+ receiver, opPackageName, enrollReason, options);
}
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_FINGERPRINT)
@@ -1322,4 +1324,25 @@ public class FingerprintService extends SystemService {
}
}
}
+
+ /**
+ * This should only be called from FingerprintShellCommand
+ */
+ void sendFingerprintReEnrollNotification() {
+ Utils.checkPermissionOrShell(getContext(), MANAGE_FINGERPRINT);
+ if (Build.IS_DEBUGGABLE) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
+ if (provider != null) {
+ FingerprintProvider fingerprintProvider = (FingerprintProvider) provider.second;
+ fingerprintProvider.sendFingerprintReEnrollNotification();
+ } else {
+ Slog.w(TAG, "Null provider for notification");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java
index dc6a63f82bb1..766b3a1f1fbf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java
@@ -47,6 +47,8 @@ public class FingerprintShellCommand extends ShellCommand {
return doSync();
case "fingerdown":
return doSimulateVhalFingerDown();
+ case "notification":
+ return doNotify();
default:
getOutPrintWriter().println("Unrecognized command: " + cmd);
}
@@ -66,6 +68,8 @@ public class FingerprintShellCommand extends ShellCommand {
pw.println(" Sync enrollments now (virtualized sensors only).");
pw.println(" fingerdown");
pw.println(" Simulate finger down event (virtualized sensors only).");
+ pw.println(" notification");
+ pw.println(" Sends a Fingerprint re-enrollment notification");
}
private int doHelp() {
@@ -82,4 +86,9 @@ public class FingerprintShellCommand extends ShellCommand {
mService.simulateVhalFingerDown();
return 0;
}
+
+ private int doNotify() {
+ mService.sendFingerprintReEnrollNotification();
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index fc37d7020a21..c2d11699b91f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -24,6 +24,7 @@ import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -74,7 +75,8 @@ public interface ServiceProvider extends
*/
long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
int userId, @NonNull IFingerprintServiceReceiver receiver,
- @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason);
+ @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason,
+ @NonNull FingerprintEnrollOptions options);
void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index ec1eeb138505..d64b6c29c840 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -16,13 +16,12 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
-import static android.Manifest.permission.TEST_BIOMETRIC;
-
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
@@ -30,7 +29,6 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
-import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -153,7 +151,8 @@ class BiometricTestSessionImpl extends ITestSession.Stub {
super.startEnroll_enforcePermission();
mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL);
+ mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL,
+ (new FingerprintEnrollOptions.Builder()).build());
}
@android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 79975e515c70..a24ab1d022c6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -29,6 +29,7 @@ import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationState;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
@@ -98,11 +99,13 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement
// TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Nullable ISidefpsController sidefpsController,
@NonNull AuthenticationStateListeners authenticationStateListeners,
- int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason) {
+ int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason,
+ @NonNull FingerprintEnrollOptions options) {
// UDFPS haptics occur when an image is acquired (instead of when the result is known)
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
0 /* timeoutSec */, sensorId, shouldVibrateFor(context, sensorProps),
- logger, biometricContext);
+ logger, biometricContext,
+ BiometricFingerprintConstants.reasonToMetric(options.getEnrollReason()));
setRequestId(requestId);
mSensorProps = sensorProps;
if (sidefpsControllerRefactor()) {
@@ -120,6 +123,8 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
getLogger().disableMetrics();
}
+ Slog.w(TAG, "EnrollOptions "
+ + FingerprintEnrollOptions.enrollReasonToString(options.getEnrollReason()));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index fd938ed9c389..a104cf4e1726 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -39,6 +39,7 @@ import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -515,7 +516,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @FingerprintManager.EnrollReason int enrollReason) {
+ @FingerprintManager.EnrollReason int enrollReason,
+ @NonNull FingerprintEnrollOptions options) {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
final int maxTemplatesPerUser = mFingerprintSensors.get(sensorId).getSensorProperties()
@@ -529,7 +531,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mBiometricContext,
mFingerprintSensors.get(sensorId).getSensorProperties(),
mUdfpsOverlayController, mSidefpsController,
- mAuthenticationStateListeners, maxTemplatesPerUser, enrollReason);
+ mAuthenticationStateListeners, maxTemplatesPerUser, enrollReason, options);
if (Flags.deHidl()) {
scheduleForSensor(sensorId, client, mBiometricStateCallback);
} else {
@@ -1021,4 +1023,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
Slog.e(getTag(), "failed hal operation ", e);
}
}
+
+ /**
+ * Sends a fingerprint enroll notification.
+ */
+ public void sendFingerprintReEnrollNotification() {
+ mAuthenticationStatsCollector.sendFingerprintReEnrollNotification();
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index c20a9eb958c4..fc037aeb7e17 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -16,20 +16,18 @@
package com.android.server.biometrics.sensors.fingerprint.hidl;
-import static android.Manifest.permission.TEST_BIOMETRIC;
-
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -153,7 +151,8 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
super.startEnroll_enforcePermission();
mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL);
+ mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL,
+ (new FingerprintEnrollOptions.Builder()).build());
}
@android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 4accf8f7ff30..33e448bbe612 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -35,6 +35,7 @@ import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -700,17 +701,18 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @FingerprintManager.EnrollReason int enrollReason) {
+ @FingerprintManager.EnrollReason int enrollReason,
+ @NonNull FingerprintEnrollOptions options) {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
if (Flags.deHidl()) {
scheduleEnrollAidl(token, hardwareAuthToken, userId, receiver,
- opPackageName, enrollReason, id);
+ opPackageName, enrollReason, id, options);
} else {
scheduleEnrollHidl(token, hardwareAuthToken, userId, receiver,
- opPackageName, enrollReason, id);
+ opPackageName, enrollReason, id, options);
}
});
return id;
@@ -719,7 +721,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
private void scheduleEnrollHidl(@NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @FingerprintManager.EnrollReason int enrollReason, long id) {
+ @FingerprintManager.EnrollReason int enrollReason, long id,
+ @NonNull FingerprintEnrollOptions options) {
final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
userId, hardwareAuthToken, opPackageName,
@@ -730,7 +733,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mBiometricContext, mUdfpsOverlayController,
// TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
mSidefpsController,
- mAuthenticationStateListeners, enrollReason);
+ mAuthenticationStateListeners, enrollReason, options);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -758,7 +761,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
private void scheduleEnrollAidl(@NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @FingerprintManager.EnrollReason int enrollReason, long id) {
+ @FingerprintManager.EnrollReason int enrollReason, long id,
+ @NonNull FingerprintEnrollOptions options) {
final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient
client =
new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient(
@@ -778,8 +782,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mAuthenticationStateListeners,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser),
- enrollReason);
-
+ enrollReason, options);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 26332ff6893c..8f937fc65cbb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -27,6 +27,7 @@ import android.hardware.biometrics.BiometricStateListener;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -75,10 +76,12 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
// TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Nullable ISidefpsController sidefpsController,
@NonNull AuthenticationStateListeners authenticationStateListeners,
- @FingerprintManager.EnrollReason int enrollReason) {
+ @FingerprintManager.EnrollReason int enrollReason,
+ @NonNull FingerprintEnrollOptions options) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, sensorId, true /* shouldVibrate */, biometricLogger,
- biometricContext);
+ biometricContext,
+ BiometricFingerprintConstants.reasonToMetric(options.getEnrollReason()));
setRequestId(requestId);
if (sidefpsControllerRefactor()) {
mSensorOverlays = new SensorOverlays(udfpsOverlayController);
@@ -91,6 +94,8 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
getLogger().disableMetrics();
}
+ Slog.w(TAG, "EnrollOptions "
+ + FingerprintEnrollOptions.enrollReasonToString(options.getEnrollReason()));
}
@Override
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
index bba5ba35dbc7..631e7518b746 100644
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -37,9 +37,11 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.DebugUtils;
import com.android.server.display.utils.DeviceConfigParsingUtils;
+import com.android.server.display.utils.SensorUtils;
import java.io.PrintWriter;
import java.util.HashMap;
@@ -79,7 +81,7 @@ class BrightnessThrottler {
// Maps the throttling ID to the data. Sourced from DisplayDeviceConfig.
@NonNull
- private HashMap<String, ThermalBrightnessThrottlingData> mDdcThermalThrottlingDataMap;
+ private Map<String, ThermalBrightnessThrottlingData> mDdcThermalThrottlingDataMap;
// Current throttling data being used.
// Null if we do not support throttling.
@@ -97,6 +99,10 @@ class BrightnessThrottler {
// The brightness throttling configuration that should be used.
private String mThermalBrightnessThrottlingDataId;
+ // Temperature Sensor to be monitored for throttling.
+ @NonNull
+ private SensorData mTempSensor;
+
// This is a collection of brightness throttling data that has been written as overrides from
// the DeviceConfig. This will always take priority over the display device config data.
// We need to store the data for every display device, so we do not need to update this each
@@ -121,17 +127,19 @@ class BrightnessThrottler {
BrightnessThrottler(Handler handler, Runnable throttlingChangeCallback, String uniqueDisplayId,
String throttlingDataId,
- @NonNull HashMap<String, ThermalBrightnessThrottlingData>
- thermalBrightnessThrottlingDataMap) {
- this(new Injector(), handler, handler, throttlingChangeCallback,
- uniqueDisplayId, throttlingDataId, thermalBrightnessThrottlingDataMap);
+ @NonNull DisplayDeviceConfig displayDeviceConfig) {
+ this(new Injector(), handler, handler, throttlingChangeCallback, uniqueDisplayId,
+ throttlingDataId,
+ displayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(),
+ displayDeviceConfig.getTempSensor());
}
@VisibleForTesting
BrightnessThrottler(Injector injector, Handler handler, Handler deviceConfigHandler,
Runnable throttlingChangeCallback, String uniqueDisplayId, String throttlingDataId,
- @NonNull HashMap<String, ThermalBrightnessThrottlingData>
- thermalBrightnessThrottlingDataMap) {
+ @NonNull Map<String, ThermalBrightnessThrottlingData>
+ thermalBrightnessThrottlingDataMap,
+ @NonNull SensorData tempSensor) {
mInjector = injector;
mHandler = handler;
@@ -147,7 +155,7 @@ class BrightnessThrottler {
mDdcThermalThrottlingDataMap = thermalBrightnessThrottlingDataMap;
loadThermalBrightnessThrottlingDataFromDeviceConfig();
loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(mDdcThermalThrottlingDataMap,
- mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
+ tempSensor, mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
}
boolean deviceSupportsThrottling() {
@@ -180,12 +188,14 @@ class BrightnessThrottler {
}
void loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
- HashMap<String, ThermalBrightnessThrottlingData> ddcThrottlingDataMap,
+ Map<String, ThermalBrightnessThrottlingData> ddcThrottlingDataMap,
+ SensorData tempSensor,
String brightnessThrottlingDataId,
String uniqueDisplayId) {
mDdcThermalThrottlingDataMap = ddcThrottlingDataMap;
mThermalBrightnessThrottlingDataId = brightnessThrottlingDataId;
mUniqueDisplayId = uniqueDisplayId;
+ mTempSensor = tempSensor;
resetThermalThrottlingData();
}
@@ -310,7 +320,7 @@ class BrightnessThrottler {
}
if (deviceSupportsThrottling()) {
- mSkinThermalStatusObserver.startObserving();
+ mSkinThermalStatusObserver.startObserving(mTempSensor);
}
}
@@ -357,6 +367,7 @@ class BrightnessThrottler {
private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
private final Injector mInjector;
private final Handler mHandler;
+ private SensorData mObserverTempSensor;
private IThermalService mThermalService;
private boolean mStarted;
@@ -371,28 +382,51 @@ class BrightnessThrottler {
if (DEBUG) {
Slog.d(TAG, "New thermal throttling status = " + temp.getStatus());
}
+
+ if (mObserverTempSensor.name != null
+ && !mObserverTempSensor.name.equals(temp.getName())) {
+ Slog.i(TAG, "Skipping thermal throttling notification as monitored sensor: "
+ + mObserverTempSensor.name
+ + " != notified sensor: "
+ + temp.getName());
+ return;
+ }
mHandler.post(() -> {
final @Temperature.ThrottlingStatus int status = temp.getStatus();
thermalStatusChanged(status);
});
}
- void startObserving() {
- if (mStarted) {
+ void startObserving(SensorData tempSensor) {
+ if (!mStarted || mObserverTempSensor == null) {
+ mObserverTempSensor = tempSensor;
+ registerThermalListener();
+ return;
+ }
+
+ String curType = mObserverTempSensor.type;
+ mObserverTempSensor = tempSensor;
+ if (curType.equals(tempSensor.type)) {
if (DEBUG) {
Slog.d(TAG, "Thermal status observer already started");
}
return;
}
+ stopObserving();
+ registerThermalListener();
+ }
+
+ void registerThermalListener() {
mThermalService = mInjector.getThermalService();
if (mThermalService == null) {
Slog.e(TAG, "Could not observe thermal status. Service not available");
return;
}
+ int temperatureType = SensorUtils.getSensorTemperatureType(mObserverTempSensor);
try {
// We get a callback immediately upon registering so there's no need to query
// for the current value.
- mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
+ mThermalService.registerThermalEventListenerWithType(this, temperatureType);
mStarted = true;
} catch (RemoteException e) {
Slog.e(TAG, "Failed to register thermal status listener", e);
@@ -418,6 +452,7 @@ class BrightnessThrottler {
void dump(PrintWriter writer) {
writer.println(" SkinThermalStatusObserver:");
writer.println(" mStarted: " + mStarted);
+ writer.println(" mObserverTempSensor: " + mObserverTempSensor);
if (mThermalService != null) {
writer.println(" ThermalService available");
} else {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index d1374a5ab9dc..9b2dcc53f456 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -384,6 +384,10 @@ import javax.xml.datatype.DatatypeConfigurationException;
* </point>
* </supportedModes>
* </proxSensor>
+ * <tempSensor>
+ * <type>DISPLAY</type>
+ * <name>VIRTUAL-SKIN-DISPLAY</name>
+ * </tempSensor>
*
* <ambientLightHorizonLong>10001</ambientLightHorizonLong>
* <ambientLightHorizonShort>2001</ambientLightHorizonShort>
@@ -625,6 +629,12 @@ public class DisplayDeviceConfig {
@Nullable
private SensorData mProximitySensor;
+ // The details of the temperature sensor associated with this display.
+ // Throttling will be based on thermal status of this sensor.
+ // For empty values default back to sensor of TYPE_SKIN.
+ @NonNull
+ private SensorData mTempSensor;
+
private final List<RefreshRateLimitation> mRefreshRateLimitations =
new ArrayList<>(2 /*initialCapacity*/);
@@ -821,10 +831,10 @@ public class DisplayDeviceConfig {
private String mLowBlockingZoneThermalMapId = null;
private String mHighBlockingZoneThermalMapId = null;
- private final HashMap<String, ThermalBrightnessThrottlingData>
+ private final Map<String, ThermalBrightnessThrottlingData>
mThermalBrightnessThrottlingDataMapByThrottlingId = new HashMap<>();
- private final HashMap<String, PowerThrottlingData>
+ private final Map<String, PowerThrottlingData>
mPowerThrottlingDataMapByThrottlingId = new HashMap<>();
private final Map<String, SparseArray<SurfaceControl.RefreshRateRange>>
@@ -1489,6 +1499,13 @@ public class DisplayDeviceConfig {
return mProximitySensor;
}
+ /**
+ * @return temperature sensor data associated with the display.
+ */
+ public SensorData getTempSensor() {
+ return mTempSensor;
+ }
+
boolean isAutoBrightnessAvailable() {
return mAutoBrightnessAvailable;
}
@@ -1539,7 +1556,7 @@ public class DisplayDeviceConfig {
/**
* @return brightness throttling configuration data for this display, for each throttling id.
*/
- public HashMap<String, ThermalBrightnessThrottlingData>
+ public Map<String, ThermalBrightnessThrottlingData>
getThermalBrightnessThrottlingDataMapByThrottlingId() {
return mThermalBrightnessThrottlingDataMapByThrottlingId;
}
@@ -1558,7 +1575,7 @@ public class DisplayDeviceConfig {
/**
* @return power throttling configuration data for this display, for each throttling id.
**/
- public HashMap<String, PowerThrottlingData>
+ public Map<String, PowerThrottlingData>
getPowerThrottlingDataMapByThrottlingId() {
return mPowerThrottlingDataMapByThrottlingId;
}
@@ -1871,6 +1888,7 @@ public class DisplayDeviceConfig {
+ "mAmbientLightSensor=" + mAmbientLightSensor
+ ", mScreenOffBrightnessSensor=" + mScreenOffBrightnessSensor
+ ", mProximitySensor=" + mProximitySensor
+ + ", mTempSensor=" + mTempSensor
+ ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
+ ", mDensityMapping= " + mDensityMapping
+ ", mAutoBrightnessBrighteningLightDebounce= "
@@ -1972,6 +1990,7 @@ public class DisplayDeviceConfig {
mContext.getResources());
mScreenOffBrightnessSensor = SensorData.loadScreenOffBrightnessSensorConfig(config);
mProximitySensor = SensorData.loadProxSensorConfig(config);
+ mTempSensor = SensorData.loadTempSensorConfig(mFlags, config);
loadAmbientHorizonFromDdc(config);
loadBrightnessChangeThresholds(config);
loadAutoBrightnessConfigValues(config);
@@ -1999,6 +2018,7 @@ public class DisplayDeviceConfig {
loadBrightnessRampsFromConfigXml();
mAmbientLightSensor = SensorData.loadAmbientLightSensorConfig(mContext.getResources());
mProximitySensor = SensorData.loadSensorUnspecifiedConfig();
+ mTempSensor = SensorData.loadTempSensorUnspecifiedConfig();
loadBrightnessChangeThresholdsFromXml();
loadAutoBrightnessConfigsFromConfigXml();
loadAutoBrightnessAvailableFromConfigXml();
@@ -2026,6 +2046,7 @@ public class DisplayDeviceConfig {
setSimpleMappingStrategyValues();
mAmbientLightSensor = SensorData.loadAmbientLightSensorConfig(mContext.getResources());
mProximitySensor = SensorData.loadSensorUnspecifiedConfig();
+ mTempSensor = SensorData.loadTempSensorUnspecifiedConfig();
loadAutoBrightnessAvailableFromConfigXml();
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index d5863a73a0c3..3965d55b0c28 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -861,6 +861,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId;
mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
config.getThermalBrightnessThrottlingDataMapByThrottlingId(),
+ config.getTempSensor(),
mThermalBrightnessThrottlingDataId,
mUniqueDisplayId);
}
@@ -923,6 +924,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig);
mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(),
+ mDisplayDeviceConfig.getTempSensor(),
mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
}
@@ -1996,7 +1998,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
postBrightnessChangeRunnable();
}, mUniqueDisplayId,
mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId,
- ddConfig.getThermalBrightnessThrottlingDataMapByThrottlingId());
+ ddConfig);
}
private void blockScreenOn() {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 3a6333099b77..88c24e0a7eff 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -842,7 +842,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// We must tell sidekick/displayoffload to stop controlling the display
// before we can change its power mode, so do that first.
if (isDisplayOffloadEnabled) {
- if (displayOffloadSession != null) {
+ if (displayOffloadSession != null
+ && !DisplayOffloadSession.isSupportedOffloadState(state)) {
displayOffloadSession.stopOffload();
}
} else {
@@ -874,8 +875,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// have a sidekick/displayoffload available, tell it now that it can take
// control.
if (isDisplayOffloadEnabled) {
- if (DisplayOffloadSession.isSupportedOffloadState(state)
- && displayOffloadSession != null) {
+ if (displayOffloadSession != null
+ && DisplayOffloadSession.isSupportedOffloadState(state)) {
displayOffloadSession.startOffload();
}
} else {
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index bc5fcb449c95..18e8fab54e3e 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -38,6 +38,7 @@ import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData;
import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.feature.DisplayManagerFlags;
@@ -336,5 +337,10 @@ public class BrightnessClamperController {
public float getBrightnessWearBedtimeModeCap() {
return mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode();
}
+
+ @NonNull
+ public SensorData getTempSensor() {
+ return mDisplayDeviceConfig.getTempSensor();
+ }
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
index 944a8a65693b..449825831182 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
@@ -35,8 +35,10 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.DeviceConfigParsingUtils;
+import com.android.server.display.utils.SensorUtils;
import java.io.PrintWriter;
import java.util.List;
@@ -49,9 +51,8 @@ class BrightnessThermalClamper extends
BrightnessClamper<BrightnessThermalClamper.ThermalData> {
private static final String TAG = "BrightnessThermalClamper";
-
- @Nullable
- private final IThermalService mThermalService;
+ @NonNull
+ private final ThermalStatusObserver mThermalStatusObserver;
@NonNull
private final DeviceConfigParameterProvider mConfigParameterProvider;
// data from DeviceConfig, for all displays, for all dataSets
@@ -66,7 +67,6 @@ class BrightnessThermalClamper extends
// otherwise mDataFromDeviceConfig
@Nullable
private ThermalBrightnessThrottlingData mThermalThrottlingDataActive = null;
- private boolean mStarted = false;
@Nullable
private String mUniqueDisplayId = null;
@Nullable
@@ -74,14 +74,6 @@ class BrightnessThermalClamper extends
@Temperature.ThrottlingStatus
private int mThrottlingStatus = Temperature.THROTTLING_NONE;
- private final IThermalEventListener mThermalEventListener = new IThermalEventListener.Stub() {
- @Override
- public void notifyThrottling(Temperature temperature) {
- @Temperature.ThrottlingStatus int status = temperature.getStatus();
- mHandler.post(() -> thermalStatusChanged(status));
- }
- };
-
private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
try {
int status = DeviceConfigParsingUtils.parseThermalStatus(key);
@@ -105,12 +97,11 @@ class BrightnessThermalClamper extends
BrightnessThermalClamper(Injector injector, Handler handler,
ClamperChangeListener listener, ThermalData thermalData) {
super(handler, listener);
- mThermalService = injector.getThermalService();
mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
+ mThermalStatusObserver = new ThermalStatusObserver(injector, handler);
mHandler.post(() -> {
setDisplayData(thermalData);
loadOverrideData();
- start();
});
}
@@ -139,32 +130,19 @@ class BrightnessThermalClamper extends
@Override
void stop() {
- if (!mStarted) {
- return;
- }
- try {
- mThermalService.unregisterThermalEventListener(mThermalEventListener);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to unregister thermal status listener", e);
- }
- mStarted = false;
+ mThermalStatusObserver.stopObserving();
}
@Override
void dump(PrintWriter writer) {
writer.println("BrightnessThermalClamper:");
- writer.println(" mStarted: " + mStarted);
- if (mThermalService != null) {
- writer.println(" ThermalService available");
- } else {
- writer.println(" ThermalService not available");
- }
writer.println(" mThrottlingStatus: " + mThrottlingStatus);
writer.println(" mUniqueDisplayId: " + mUniqueDisplayId);
writer.println(" mDataId: " + mDataId);
writer.println(" mDataOverride: " + mThermalThrottlingDataOverride);
writer.println(" mDataFromDeviceConfig: " + mThermalThrottlingDataFromDeviceConfig);
writer.println(" mDataActive: " + mThermalThrottlingDataActive);
+ mThermalStatusObserver.dump(writer);
super.dump(writer);
}
@@ -193,6 +171,7 @@ class BrightnessThermalClamper extends
Slog.wtf(TAG,
"Thermal throttling data is missing for thermalThrottlingDataId=" + mDataId);
}
+ mThermalStatusObserver.registerSensor(data.getTempSensor());
}
private void recalculateBrightnessCap() {
@@ -226,19 +205,91 @@ class BrightnessThermalClamper extends
}
}
- private void start() {
- if (mThermalService == null) {
- Slog.e(TAG, "Could not observe thermal status. Service not available");
- return;
+
+ private final class ThermalStatusObserver extends IThermalEventListener.Stub {
+ private final Injector mInjector;
+ private final Handler mHandler;
+ private IThermalService mThermalService;
+ private boolean mStarted;
+ private SensorData mObserverTempSensor;
+
+ ThermalStatusObserver(Injector injector, Handler handler) {
+ mInjector = injector;
+ mHandler = handler;
+ mStarted = false;
}
- try {
- // We get a callback immediately upon registering so there's no need to query
- // for the current value.
- mThermalService.registerThermalEventListenerWithType(mThermalEventListener,
- Temperature.TYPE_SKIN);
- mStarted = true;
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to register thermal status listener", e);
+
+ void registerSensor(SensorData tempSensor) {
+ if (!mStarted || mObserverTempSensor == null) {
+ mObserverTempSensor = tempSensor;
+ registerThermalListener();
+ return;
+ }
+
+ String curType = mObserverTempSensor.type;
+ mObserverTempSensor = tempSensor;
+ if (curType.equals(tempSensor.type)) {
+ Slog.d(TAG, "Thermal status observer already started");
+ return;
+ }
+ stopObserving();
+ registerThermalListener();
+ }
+
+ void registerThermalListener() {
+ mThermalService = mInjector.getThermalService();
+ if (mThermalService == null) {
+ Slog.e(TAG, "Could not observe thermal status. Service not available");
+ return;
+ }
+ int temperatureType = SensorUtils.getSensorTemperatureType(mObserverTempSensor);
+ try {
+ // We get a callback immediately upon registering so there's no need to query
+ // for the current value.
+ mThermalService.registerThermalEventListenerWithType(this, temperatureType);
+ mStarted = true;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register thermal status listener", e);
+ }
+ }
+
+ @Override
+ public void notifyThrottling(Temperature temp) {
+ Slog.d(TAG, "New thermal throttling status = " + temp.getStatus());
+ if (mObserverTempSensor.name != null
+ && !mObserverTempSensor.name.equals(temp.getName())) {
+ Slog.i(TAG, "Skipping thermal throttling notification as monitored sensor: "
+ + mObserverTempSensor.name
+ + " != notified sensor: "
+ + temp.getName());
+ return;
+ }
+ @Temperature.ThrottlingStatus int status = temp.getStatus();
+ mHandler.post(() -> thermalStatusChanged(status));
+ }
+
+ void stopObserving() {
+ if (!mStarted) {
+ return;
+ }
+ try {
+ mThermalService.unregisterThermalEventListener(this);
+ mStarted = false;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to unregister thermal status listener", e);
+ }
+ mThermalService = null;
+ }
+
+ void dump(PrintWriter writer) {
+ writer.println(" ThermalStatusObserver:");
+ writer.println(" mStarted: " + mStarted);
+ writer.println(" mObserverTempSensor: " + mObserverTempSensor);
+ if (mThermalService != null) {
+ writer.println(" ThermalService available");
+ } else {
+ writer.println(" ThermalService not available");
+ }
}
}
@@ -251,6 +302,9 @@ class BrightnessThermalClamper extends
@Nullable
ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData();
+
+ @NonNull
+ SensorData getTempSensor();
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/display/config/SensorData.java b/services/core/java/com/android/server/display/config/SensorData.java
index 3bb35bf7c49f..8e716f8380b6 100644
--- a/services/core/java/com/android/server/display/config/SensorData.java
+++ b/services/core/java/com/android/server/display/config/SensorData.java
@@ -22,6 +22,7 @@ import android.content.res.Resources;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.feature.DisplayManagerFlags;
import java.util.ArrayList;
import java.util.Collections;
@@ -32,6 +33,9 @@ import java.util.List;
*/
public class SensorData {
+ public static final String TEMPERATURE_TYPE_DISPLAY = "DISPLAY";
+ public static final String TEMPERATURE_TYPE_SKIN = "SKIN";
+
@Nullable
public final String type;
@Nullable
@@ -143,6 +147,32 @@ public class SensorData {
}
/**
+ * Loads temperature sensor data for no config case. (Type: SKIN, Name: null)
+ */
+ public static SensorData loadTempSensorUnspecifiedConfig() {
+ return new SensorData(TEMPERATURE_TYPE_SKIN, null);
+ }
+
+ /**
+ * Loads temperature sensor data from given display config.
+ * If empty or null config given default to (Type: SKIN, Name: null)
+ */
+ public static SensorData loadTempSensorConfig(DisplayManagerFlags flags,
+ DisplayConfiguration config) {
+ SensorDetails sensorDetails = config.getTempSensor();
+ if (!flags.isSensorBasedBrightnessThrottlingEnabled() || sensorDetails == null) {
+ return new SensorData(TEMPERATURE_TYPE_SKIN, null);
+ }
+ String name = sensorDetails.getName();
+ String type = sensorDetails.getType();
+ if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
+ type = TEMPERATURE_TYPE_SKIN;
+ name = null;
+ }
+ return new SensorData(type, name);
+ }
+
+ /**
* Loads sensor unspecified config, this means system should use default sensor.
* See also {@link com.android.server.display.utils.SensorUtils}
*/
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 1ae255933f66..516d4b1d4125 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -121,6 +121,11 @@ public class DisplayManagerFlags {
Flags::refreshRateVotingTelemetry
);
+ private final FlagState mSensorBasedBrightnessThrottling = new FlagState(
+ Flags.FLAG_SENSOR_BASED_BRIGHTNESS_THROTTLING,
+ Flags::sensorBasedBrightnessThrottling
+ );
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -247,6 +252,10 @@ public class DisplayManagerFlags {
return mRefreshRateVotingTelemetry.isEnabled();
}
+ public boolean isSensorBasedBrightnessThrottlingEnabled() {
+ return mSensorBasedBrightnessThrottling.isEnabled();
+ }
+
/**
* dumps all flagstates
* @param pw printWriter
@@ -270,6 +279,7 @@ public class DisplayManagerFlags {
pw.println(" " + mAutoBrightnessModesFlagState);
pw.println(" " + mFastHdrTransitions);
pw.println(" " + mRefreshRateVotingTelemetry);
+ pw.println(" " + mSensorBasedBrightnessThrottling);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index c2f52b5ad8a0..63ab3a95822f 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -184,3 +184,11 @@ flag {
bug: "310029108"
is_fixed_read_only: true
}
+
+flag {
+ name: "sensor_based_brightness_throttling"
+ namespace: "display_manager"
+ description: "Feature flag for enabling brightness throttling using sensor from config."
+ bug: "294900859"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/utils/SensorUtils.java b/services/core/java/com/android/server/display/utils/SensorUtils.java
index 8b9fe1083187..c63473a4b3d7 100644
--- a/services/core/java/com/android/server/display/utils/SensorUtils.java
+++ b/services/core/java/com/android/server/display/utils/SensorUtils.java
@@ -16,9 +16,11 @@
package com.android.server.display.utils;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.Sensor;
import android.hardware.SensorManager;
+import android.os.Temperature;
import android.text.TextUtils;
import com.android.server.display.config.SensorData;
@@ -70,4 +72,17 @@ public class SensorUtils {
return null;
}
+ /**
+ * Convert string temperature type to its corresponding integer value.
+ */
+ public static int getSensorTemperatureType(@NonNull SensorData tempSensor) {
+ if (tempSensor.type.equalsIgnoreCase(SensorData.TEMPERATURE_TYPE_DISPLAY)) {
+ return Temperature.TYPE_DISPLAY;
+ } else if (tempSensor.type.equalsIgnoreCase(SensorData.TEMPERATURE_TYPE_SKIN)) {
+ return Temperature.TYPE_SKIN;
+ }
+ throw new IllegalArgumentException(
+ "tempSensor doesn't support type: " + tempSensor.type);
+ }
+
}
diff --git a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
index 3ffd2e1dec71..b30f5ecb2dd5 100644
--- a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
+++ b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
@@ -313,7 +313,7 @@ public class FocusEventDebugView extends RelativeLayout {
case KeyEvent.KEYCODE_FORWARD_DEL:
return "\u2326";
case KeyEvent.KEYCODE_ESCAPE:
- return "ESC";
+ return "esc";
case KeyEvent.KEYCODE_DPAD_UP:
return "\u2191";
case KeyEvent.KEYCODE_DPAD_DOWN:
@@ -330,6 +330,14 @@ public class FocusEventDebugView extends RelativeLayout {
return "\u2198";
case KeyEvent.KEYCODE_DPAD_DOWN_LEFT:
return "\u2199";
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ return "\u23ef";
+ case KeyEvent.KEYCODE_HOME:
+ return "\u25ef";
+ case KeyEvent.KEYCODE_BACK:
+ return "\u25c1";
+ case KeyEvent.KEYCODE_RECENT_APPS:
+ return "\u25a1";
default:
break;
}
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
index 84a59b4d28e4..7251ac42c582 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
@@ -43,6 +43,9 @@ import com.android.internal.inputmethod.InputBindResult;
* the given {@link Handler} thread if {@link IInputMethodClient} is not a proxy object. Be careful
* about its call ordering characteristics.</p>
*/
+// TODO(b/322895594) Mark this class to be host side test compatible once enabling fw/services in
+// Ravenwood (mark this class with @RavenwoodKeepWholeClass and #create with @RavenwoodReplace,
+// so Ravenwood can properly swap create method during test execution).
final class IInputMethodClientInvoker {
private static final String TAG = InputMethodManagerService.TAG;
private static final boolean DEBUG = InputMethodManagerService.DEBUG;
@@ -64,6 +67,16 @@ final class IInputMethodClientInvoker {
return new IInputMethodClientInvoker(inputMethodClient, isProxy, isProxy ? null : handler);
}
+ @AnyThread
+ @Nullable
+ static IInputMethodClientInvoker create$ravenwood(
+ @Nullable IInputMethodClient inputMethodClient, @NonNull Handler handler) {
+ if (inputMethodClient == null) {
+ return null;
+ }
+ return new IInputMethodClientInvoker(inputMethodClient, true, null);
+ }
+
private IInputMethodClientInvoker(@NonNull IInputMethodClient target,
boolean isProxy, @Nullable Handler handler) {
mTarget = target;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b4d15e968249..76956c88695d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -148,6 +148,7 @@ import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
@@ -2435,20 +2436,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return InputBindResult.NOT_IME_TARGET_WINDOW;
}
final int csDisplayId = cs.mSelfReportedDisplayId;
- final int oldDisplayIdToShowIme = mDisplayIdToShowIme;
mDisplayIdToShowIme = mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId);
// Potentially override the selected input method if the new display belongs to a virtual
// device with a custom IME.
String selectedMethodId = getSelectedMethodIdLocked();
- if (oldDisplayIdToShowIme != mDisplayIdToShowIme) {
- final String deviceMethodId = computeCurrentDeviceMethodIdLocked(selectedMethodId);
- if (deviceMethodId == null) {
- mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);
- } else if (!Objects.equals(deviceMethodId, selectedMethodId)) {
- setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, mDeviceIdToShowIme);
- selectedMethodId = deviceMethodId;
- }
+ final String deviceMethodId = computeCurrentDeviceMethodIdLocked(selectedMethodId);
+ if (deviceMethodId == null) {
+ mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);
+ } else if (!Objects.equals(deviceMethodId, selectedMethodId)) {
+ setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, mDeviceIdToShowIme);
+ selectedMethodId = deviceMethodId;
}
if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) {
@@ -2548,10 +2546,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final int oldDeviceId = mDeviceIdToShowIme;
mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme);
- if (mDeviceIdToShowIme == oldDeviceId) {
- return currentMethodId;
- }
if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+ if (oldDeviceId == DEVICE_ID_DEFAULT) {
+ return currentMethodId;
+ }
final String defaultDeviceMethodId = mSettings.getSelectedDefaultDeviceInputMethod();
if (DEBUG) {
Slog.v(TAG, "Restoring default device input method: " + defaultDeviceMethodId);
@@ -3544,6 +3542,19 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
+ public void acceptStylusHandwritingDelegationAsync(
+ @NonNull IInputMethodClient client,
+ @UserIdInt int userId,
+ @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags, IBooleanListener callback)
+ throws RemoteException {
+ boolean result = acceptStylusHandwritingDelegation(
+ client, userId, delegatePackageName, delegatorPackageName, flags);
+ callback.onResult(result);
+ }
+
+ @Override
public boolean acceptStylusHandwritingDelegation(
@NonNull IInputMethodClient client,
@UserIdInt int userId,
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
index 692fd7dcceae..62d44557ae4c 100644
--- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -58,6 +58,7 @@ import android.view.inputmethod.InputMethodSubtype;
import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInputMethodClient;
@@ -182,6 +183,7 @@ public class ZeroJankProxy extends IInputMethodManager.Stub {
return true;
}
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
@Override
public void startInputOrWindowGainedFocusAsync(
@StartInputReason int startInputReason,
@@ -346,6 +348,18 @@ public class ZeroJankProxy extends IInputMethodManager.Stub {
}
@Override
+ public void acceptStylusHandwritingDelegationAsync(
+ @NonNull IInputMethodClient client,
+ @UserIdInt int userId,
+ @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags, IBooleanListener callback)
+ throws RemoteException {
+ offload(() -> mInner.acceptStylusHandwritingDelegationAsync(
+ client, userId, delegatePackageName, delegatorPackageName, flags, callback));
+ }
+
+ @Override
public void prepareStylusHandwritingDelegation(
@NonNull IInputMethodClient client,
@UserIdInt int userId,
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index c5f38553ed81..75be0684c8f4 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -1346,7 +1346,9 @@ public class LocationManagerService extends ILocationManager.Stub implements
"setAutomotiveGnssSuspended only allowed on automotive devices");
}
- mGnssManagerService.setAutomotiveGnssSuspended(suspended);
+ if (mGnssManagerService != null) {
+ mGnssManagerService.setAutomotiveGnssSuspended(suspended);
+ }
}
@android.annotation.EnforcePermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS)
@@ -1361,7 +1363,10 @@ public class LocationManagerService extends ILocationManager.Stub implements
"isAutomotiveGnssSuspended only allowed on automotive devices");
}
- return mGnssManagerService.isAutomotiveGnssSuspended();
+ if (mGnssManagerService != null) {
+ return mGnssManagerService.isAutomotiveGnssSuspended();
+ }
+ return false;
}
@Override
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 34bb219a9162..a110e5637f82 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -40,6 +40,7 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Object mLock = new Object();
+ private final int mUniqueId;
@GuardedBy("mLock")
private final Session2Token mSessionToken;
@GuardedBy("mLock")
@@ -63,11 +64,13 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
MediaSessionService service,
Looper handlerLooper,
int pid,
- int policies) {
+ int policies,
+ int uniqueId) {
// The lock is required to prevent `Controller2Callback` from using partially initialized
// `MediaSession2Record.this`.
synchronized (mLock) {
mSessionToken = sessionToken;
+ mUniqueId = uniqueId;
mService = service;
mHandlerExecutor = new HandlerExecutor(new Handler(handlerLooper));
mController = new MediaController2.Builder(service.getContext(), sessionToken)
@@ -98,6 +101,13 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
}
@Override
+ public int getUniqueId() {
+ synchronized (mLock) {
+ return mUniqueId;
+ }
+ }
+
+ @Override
public String getPackageName() {
return mSessionToken.getPackageName();
}
@@ -144,7 +154,7 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
@Override
public boolean checkPlaybackActiveState(boolean expected) {
synchronized (mLock) {
- return mIsConnected && mController.isPlaybackActive() == expected;
+ return (mIsConnected && mController.isPlaybackActive()) == expected;
}
}
@@ -200,6 +210,7 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
@Override
public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "uniqueId=" + mUniqueId);
pw.println(prefix + "token=" + mSessionToken);
pw.println(prefix + "controller=" + mController);
@@ -209,8 +220,7 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
@Override
public String toString() {
- // TODO(jaewan): Also add getId().
- return getPackageName() + " (userId=" + getUserId() + ")";
+ return getPackageName() + "/" + mUniqueId + " (userId=" + getUserId() + ")";
}
private class Controller2Callback extends MediaController2.ControllerCallback {
@@ -224,10 +234,6 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
mIsConnected = true;
service = mService;
}
-
- // TODO (b/318745416): Add support for FGS in MediaSession2. Passing a
- // null playback state means the owning process will not be allowed to
- // run in the foreground.
service.onSessionActiveStateChanged(MediaSession2Record.this,
/* playbackState= */ null);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 53f780e4d19e..15527041d8eb 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -173,6 +173,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
private final int mUserId;
private final String mPackageName;
private final String mTag;
+ private final int mUniqueId;
private final Bundle mSessionInfo;
private final ControllerStub mController;
private final MediaSession.Token mSessionToken;
@@ -223,15 +224,25 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
private int mPolicies;
- public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
- ISessionCallback cb, String tag, Bundle sessionInfo,
- MediaSessionService service, Looper handlerLooper, int policies)
+ public MediaSessionRecord(
+ int ownerPid,
+ int ownerUid,
+ int userId,
+ String ownerPackageName,
+ ISessionCallback cb,
+ String tag,
+ int uniqueId,
+ Bundle sessionInfo,
+ MediaSessionService service,
+ Looper handlerLooper,
+ int policies)
throws RemoteException {
mOwnerPid = ownerPid;
mOwnerUid = ownerUid;
mUserId = userId;
mPackageName = ownerPackageName;
mTag = tag;
+ mUniqueId = uniqueId;
mSessionInfo = sessionInfo;
mController = new ControllerStub();
mSessionToken = new MediaSession.Token(ownerUid, mController);
@@ -292,6 +303,16 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
/**
+ * Get the unique id of this session record.
+ *
+ * @return a unique id of this session record.
+ */
+ @Override
+ public int getUniqueId() {
+ return mUniqueId;
+ }
+
+ /**
* Get the info for this session.
*
* @return Info that identifies this session.
@@ -703,7 +724,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
@Override
public String toString() {
- return mPackageName + "/" + mTag + " (userId=" + mUserId + ")";
+ return mPackageName + "/" + mTag + "/" + mUniqueId + " (userId=" + mUserId + ")";
}
@Override
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 99c8ea93936e..e53a2dbe8101 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -32,6 +32,13 @@ import java.io.PrintWriter;
public interface MediaSessionRecordImpl extends AutoCloseable {
/**
+ * Get the unique id of this session record.
+ *
+ * @return a unique id of this session record.
+ */
+ int getUniqueId();
+
+ /**
* Get the info for this session.
*
* @return Info that identifies this session.
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index e7e8096a1965..9e98a5809650 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -106,6 +106,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* System implementation of MediaSessionManager
@@ -155,6 +156,8 @@ public class MediaSessionService extends SystemService implements Monitor {
/* Maps uid with all user engaging session tokens associated to it */
private final SparseArray<Set<MediaSession.Token>> mUserEngagingSessions = new SparseArray<>();
+ private final AtomicInteger mNextMediaSessionRecordId = new AtomicInteger(1);
+
// The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
// It's always not null after the MediaSessionService is started.
private FullUserRecord mCurrentFullUserRecord;
@@ -193,7 +196,8 @@ public class MediaSessionService extends SystemService implements Monitor {
MediaSessionService.this,
mRecordThread.getLooper(),
pid,
- /* policies= */ 0);
+ /* policies= */ 0,
+ /* uniqueId= */ mNextMediaSessionRecordId.getAndIncrement());
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(record.getUserId());
if (user != null) {
@@ -312,8 +316,7 @@ public class MediaSessionService extends SystemService implements Monitor {
}
user.mPriorityStack.onSessionActiveStateChanged(record);
}
- boolean isUserEngaged =
- record.isActive() && (playbackState == null || playbackState.isActive());
+ boolean isUserEngaged = isUserEngaged(record, playbackState);
Log.d(TAG, "onSessionActiveStateChanged: "
+ "record=" + record
@@ -325,6 +328,15 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
+ private boolean isUserEngaged(MediaSessionRecordImpl record,
+ @Nullable PlaybackState playbackState) {
+ if (playbackState == null) {
+ // MediaSession2 case
+ return record.checkPlaybackActiveState(/* expected= */ true);
+ }
+ return playbackState.isActive() && record.isActive();
+ }
+
// Currently only media1 can become global priority session.
void setGlobalPrioritySession(MediaSessionRecord record) {
synchronized (mLock) {
@@ -417,16 +429,13 @@ public class MediaSessionService extends SystemService implements Monitor {
return;
}
user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority);
- if (playbackState != null) {
- boolean isUserEngaged = playbackState.isActive() && record.isActive();
- Log.d(TAG, "onSessionPlaybackStateChanged: "
- + "record=" + record
- + "playbackState=" + playbackState
- + "allowRunningInForeground=" + isUserEngaged);
- setForegroundServiceAllowance(
- record, /* allowRunningInForeground= */ isUserEngaged);
- reportMediaInteractionEvent(record, isUserEngaged);
- }
+ boolean isUserEngaged = isUserEngaged(record, playbackState);
+ Log.d(TAG, "onSessionPlaybackStateChanged: "
+ + "record=" + record
+ + "playbackState=" + playbackState
+ + "allowRunningInForeground=" + isUserEngaged);
+ setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged);
+ reportMediaInteractionEvent(record, isUserEngaged);
}
}
@@ -789,9 +798,19 @@ public class MediaSessionService extends SystemService implements Monitor {
final MediaSessionRecord session;
try {
- session = new MediaSessionRecord(callerPid, callerUid, userId,
- callerPackageName, cb, tag, sessionInfo, this,
- mRecordThread.getLooper(), policies);
+ session =
+ new MediaSessionRecord(
+ callerPid,
+ callerUid,
+ userId,
+ callerPackageName,
+ cb,
+ tag,
+ /* uniqueId= */ mNextMediaSessionRecordId.getAndIncrement(),
+ sessionInfo,
+ this,
+ mRecordThread.getLooper(),
+ policies);
} catch (RemoteException e) {
throw new RuntimeException("Media Session owner died prematurely.", e);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7455fe0043f8..fc7b87317344 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1355,7 +1355,8 @@ public class NotificationManagerService extends SystemService {
nv.recycle();
reportUserInteraction(r);
mAssistants.notifyAssistantActionClicked(r, action, generatedByAssistant);
- // Notifications that have been interacted with don't need to be lifetime extended.
+ // Notifications that have been interacted with should no longer be lifetime
+ // extended.
if (lifetimeExtensionRefactor()) {
r.getSbn().getNotification().flags &= ~FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
}
@@ -1524,9 +1525,32 @@ public class NotificationManagerService extends SystemService {
@Override
public void onNotificationDirectReplied(String key) {
exitIdle();
+ String packageName = null;
+ final int packageImportance;
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
+ packageName = r.getSbn().getPackageName();
+ }
+ }
+ if (lifetimeExtensionRefactor() && packageName != null) {
+ packageImportance = getPackageImportanceWithIdentity(packageName);
+ } else {
+ packageImportance = IMPORTANCE_NONE;
+ }
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ // If the notification is already marked as lifetime extended before we record
+ // the new direct reply, there must have been a previous lifetime extension
+ // event, and the app has already cancelled the notification, or does not
+ // respond to direct replies with updates. So we need to update System UI
+ // immediately.
+ if (lifetimeExtensionRefactor()) {
+ maybeNotifySystemUiListenerLifetimeExtendedLocked(r,
+ r.getSbn().getPackageName(), packageImportance);
+ }
+
r.recordDirectReplied();
mMetricsLogger.write(r.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
@@ -1557,10 +1581,31 @@ public class NotificationManagerService extends SystemService {
@Override
public void onNotificationSmartReplySent(String key, int replyIndex, CharSequence reply,
int notificationLocation, boolean modifiedBeforeSending) {
-
+ String packageName = null;
+ final int packageImportance;
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
+ packageName = r.getSbn().getPackageName();
+ }
+ }
+ if (lifetimeExtensionRefactor() && packageName != null) {
+ packageImportance = getPackageImportanceWithIdentity(packageName);
+ } else {
+ packageImportance = IMPORTANCE_NONE;
+ }
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ // If the notification is already marked as lifetime extended before we record
+ // the new direct reply, there must have been a previous lifetime extension
+ // event, and the app has already cancelled the notification, or does not
+ // respond to direct replies with updates. So we need to update System UI
+ // immediately.
+ if (lifetimeExtensionRefactor()) {
+ maybeNotifySystemUiListenerLifetimeExtendedLocked(r,
+ r.getSbn().getPackageName(), packageImportance);
+ }
r.recordSmartReplied();
LogMaker logMaker = r.getLogMaker()
.setCategory(MetricsEvent.SMART_REPLY_ACTION)
@@ -7735,7 +7780,7 @@ public class NotificationManagerService extends SystemService {
notification.flags &= ~FLAG_FSI_REQUESTED_BUT_DENIED;
- // Apps should not create notifications that are lifetime extended.
+ // Apps cannot post notifications that are lifetime extended.
if (lifetimeExtensionRefactor()) {
notification.flags &= ~FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
}
@@ -12005,8 +12050,10 @@ public class NotificationManagerService extends SystemService {
@Override
public void onServiceAdded(ManagedServiceInfo info) {
if (lifetimeExtensionRefactor()) {
- // Only System or System UI can call registerSystemService, so if the caller is not
- // system, we know it's system UI.
+ // Generally, only System or System UI should have the permissions to call
+ // registerSystemService.
+ // isCallerSystemorPhone tells us whether the caller is System. Then, if it's not
+ // the system, we know it's system UI.
info.isSystemUi = !isCallerSystemOrPhone();
}
final INotificationListener listener = (INotificationListener) info.service;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index efb8c84d788a..eed0eecb0a22 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -123,6 +123,7 @@ import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -593,20 +594,20 @@ public class ZenModeHelper {
if (mConfig == null) {
return;
}
+ ZenModeConfig newConfig = mConfig.copy();
+ ZenRule rule = newConfig.automaticRules.get(implicitRuleId(callingPkg));
if (zenMode == Global.ZEN_MODE_OFF) {
// Deactivate implicit rule if it exists and is active; otherwise ignore.
- ZenRule rule = mConfig.automaticRules.get(implicitRuleId(callingPkg));
if (rule != null) {
Condition deactivated = new Condition(rule.conditionId,
mContext.getString(R.string.zen_mode_implicit_deactivated),
Condition.STATE_FALSE);
- setAutomaticZenRuleState(rule.id, deactivated, UPDATE_ORIGIN_APP, callingUid);
+ setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule),
+ deactivated, UPDATE_ORIGIN_APP, callingUid);
}
} else {
// Either create a new rule with a default ZenPolicy, or update an existing rule's
// filter value. In both cases, also activate (and unsnooze) it.
- ZenModeConfig newConfig = mConfig.copy();
- ZenRule rule = newConfig.automaticRules.get(implicitRuleId(callingPkg));
if (rule == null) {
rule = newImplicitZenRule(callingPkg);
@@ -878,9 +879,17 @@ public class ZenModeHelper {
if (mConfig == null) return;
newConfig = mConfig.copy();
- ArrayList<ZenRule> rules = new ArrayList<>();
- rules.add(newConfig.automaticRules.get(id));
- setAutomaticZenRuleStateLocked(newConfig, rules, condition, origin, callingUid);
+ ZenRule rule = newConfig.automaticRules.get(id);
+ if (Flags.modesApi()) {
+ if (rule != null && canManageAutomaticZenRule(rule)) {
+ setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule),
+ condition, origin, callingUid);
+ }
+ } else {
+ ArrayList<ZenRule> rules = new ArrayList<>();
+ rules.add(rule); // rule may be null and throw NPE in the next method.
+ setAutomaticZenRuleStateLocked(newConfig, rules, condition, origin, callingUid);
+ }
}
}
@@ -892,9 +901,15 @@ public class ZenModeHelper {
if (mConfig == null) return;
newConfig = mConfig.copy();
- setAutomaticZenRuleStateLocked(newConfig,
- findMatchingRules(newConfig, ruleDefinition, condition),
- condition, origin, callingUid);
+ List<ZenRule> matchingRules = findMatchingRules(newConfig, ruleDefinition, condition);
+ if (Flags.modesApi()) {
+ for (int i = matchingRules.size() - 1; i >= 0; i--) {
+ if (!canManageAutomaticZenRule(matchingRules.get(i))) {
+ matchingRules.remove(i);
+ }
+ }
+ }
+ setAutomaticZenRuleStateLocked(newConfig, matchingRules, condition, origin, callingUid);
}
}
@@ -914,8 +929,9 @@ public class ZenModeHelper {
}
}
- private List<ZenRule> findMatchingRules(ZenModeConfig config, Uri id, Condition condition) {
- List<ZenRule> matchingRules= new ArrayList<>();
+ private static List<ZenRule> findMatchingRules(ZenModeConfig config, Uri id,
+ Condition condition) {
+ List<ZenRule> matchingRules = new ArrayList<>();
if (ruleMatches(id, condition, config.manualRule)) {
matchingRules.add(config.manualRule);
} else {
@@ -928,7 +944,7 @@ public class ZenModeHelper {
return matchingRules;
}
- private boolean ruleMatches(Uri id, Condition condition, ZenRule rule) {
+ private static boolean ruleMatches(Uri id, Condition condition, ZenRule rule) {
if (id == null || rule == null || rule.conditionId == null) return false;
if (!rule.conditionId.equals(id)) return false;
if (Objects.equals(condition, rule.condition)) return false;
@@ -1880,12 +1896,14 @@ public class ZenModeHelper {
if (rule.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
policy.apply(new ZenPolicy.Builder()
.disallowAllSounds()
+ .allowPriorityChannels(false)
.build());
} else if (rule.zenMode == Global.ZEN_MODE_ALARMS) {
policy.apply(new ZenPolicy.Builder()
.disallowAllSounds()
.allowAlarms(true)
.allowMedia(true)
+ .allowPriorityChannels(false)
.build());
} else if (rule.zenPolicy != null) {
policy.apply(rule.zenPolicy);
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 53244f9e5ecf..43361ed97bc6 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -68,4 +68,14 @@ flag {
namespace: "systemui"
description: "This flag enables memory restriction of notifications holding custom views with Uri Bitmaps"
bug: "270553691"
+}
+
+flag {
+ name: "notification_hide_unused_channels"
+ namespace: "systemui"
+ description: "By default, hide non-blocked notification channels that haven't sent a notification in the last 2 weeks"
+ bug: "322536537"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
index 59d3d1746754..5ad550722c93 100644
--- a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
@@ -656,8 +656,10 @@ public class PersistentDataBlockService extends SystemService {
@VisibleForTesting
boolean isFrpActive() {
- waitForInitDoneSignal();
synchronized (mLock) {
+ // mFrpActive is initialized and automatic deactivation done (if possible) before the
+ // service is published, so there's no chance that callers could ask for the state
+ // before it has settled.
return mFrpActive;
}
}
@@ -1253,6 +1255,7 @@ public class PersistentDataBlockService extends SystemService {
private void enforceFactoryResetProtectionInactive() {
if (mFrpEnforced && isFrpActive()) {
+ Slog.w(TAG, "Attempt to update PDB was blocked because FRP is active.");
throw new SecurityException("FRP is active");
}
}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 5b3f7a58e653..017cf55541fc 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -20,6 +20,9 @@ import static android.content.pm.PackageManager.ONLY_IF_NO_MATCH_FOUND;
import static android.content.pm.PackageManager.SKIP_CURRENT_PROFILE;
import static android.speech.RecognizerIntent.ACTION_RECOGNIZE_SPEECH;
+import static com.android.server.pm.CrossProfileIntentFilter.FLAG_ALLOW_CHAINED_RESOLUTION;
+import static com.android.server.pm.CrossProfileIntentFilter.FLAG_IS_PACKAGE_FOR_FILTER;
+
import android.content.Intent;
import android.hardware.usb.UsbManager;
import android.provider.AlarmClock;
@@ -613,6 +616,27 @@ public class DefaultCrossProfileIntentFiltersUtils {
.addDataScheme("mmsto")
.build();
+ private static final DefaultCrossProfileIntentFilter CLONE_TO_PARENT_ACTION_PICK_IMAGES =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */ FLAG_IS_PACKAGE_FOR_FILTER | FLAG_ALLOW_CHAINED_RESOLUTION,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(MediaStore.ACTION_PICK_IMAGES)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ private static final DefaultCrossProfileIntentFilter
+ CLONE_TO_PARENT_ACTION_PICK_IMAGES_WITH_DATA_TYPES =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */ FLAG_IS_PACKAGE_FOR_FILTER | FLAG_ALLOW_CHAINED_RESOLUTION,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(MediaStore.ACTION_PICK_IMAGES)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addDataType("image/*")
+ .addDataType("video/*")
+ .build();
+
public static List<DefaultCrossProfileIntentFilter> getDefaultCloneProfileFilters() {
return Arrays.asList(
PARENT_TO_CLONE_SEND_ACTION,
@@ -626,7 +650,80 @@ public class DefaultCrossProfileIntentFiltersUtils {
CLONE_TO_PARENT_PICK_INSERT_ACTION,
CLONE_TO_PARENT_DIAL_DATA,
CLONE_TO_PARENT_SMS_MMS,
- CLONE_TO_PARENT_PHOTOPICKER_SELECTION
+ CLONE_TO_PARENT_PHOTOPICKER_SELECTION,
+ CLONE_TO_PARENT_ACTION_PICK_IMAGES,
+ CLONE_TO_PARENT_ACTION_PICK_IMAGES_WITH_DATA_TYPES
+ );
+ }
+
+ /** Dial intent with mime type can be handled by either private profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter DIAL_MIME_PRIVATE_PROFILE =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataType("vnd.android.cursor.item/phone")
+ .addDataType("vnd.android.cursor.item/phone_v2")
+ .addDataType("vnd.android.cursor.item/person")
+ .addDataType("vnd.android.cursor.dir/calls")
+ .addDataType("vnd.android.cursor.item/calls")
+ .build();
+
+ /** Dial intent with data scheme can be handled by either private profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter DIAL_DATA_PRIVATE_PROFILE =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("tel")
+ .addDataScheme("sip")
+ .addDataScheme("voicemail")
+ .build();
+
+ /**
+ * Dial intent with no data scheme or type can be handled by either private profile or its
+ * parent user.
+ */
+ private static final DefaultCrossProfileIntentFilter DIAL_RAW_PRIVATE_PROFILE =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .build();
+
+ /** SMS and MMS can be handled by the private profile or by the parent user. */
+ private static final DefaultCrossProfileIntentFilter SMS_MMS_PRIVATE_PROFILE =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_VIEW)
+ .addAction(Intent.ACTION_SENDTO)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("sms")
+ .addDataScheme("smsto")
+ .addDataScheme("mms")
+ .addDataScheme("mmsto")
+ .build();
+
+ public static List<DefaultCrossProfileIntentFilter> getDefaultPrivateProfileFilters() {
+ return Arrays.asList(
+ DIAL_MIME_PRIVATE_PROFILE,
+ DIAL_DATA_PRIVATE_PROFILE,
+ DIAL_RAW_PRIVATE_PROFILE,
+ SMS_MMS_PRIVATE_PROFILE
);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index df4e5a319ee6..cdd52a47e433 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -62,6 +62,7 @@ import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -85,6 +86,7 @@ import android.os.RemoteException;
import android.os.SELinux;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.text.TextUtils;
import android.util.ExceptionUtils;
import android.util.Pair;
@@ -164,6 +166,9 @@ public class PackageArchiver {
@Nullable
private AppOpsManager mAppOpsManager;
+ @Nullable
+ private UserManager mUserManager;
+
/* IntentSender store that maps key: {userId, appPackageName} to respective existing attached
unarchival intent sender. */
private final Map<Pair<Integer, String>, IntentSender> mLauncherIntentSenders;
@@ -276,12 +281,8 @@ public class PackageArchiver {
Slog.e(TAG, "callerPackageName cannot be null for unarchival!");
return START_CLASS_NOT_FOUND;
}
- if (!isCallingPackageValid(callerPackageName, callingUid, userId)) {
- // Return early as the calling UID does not match caller package's UID.
- return START_CLASS_NOT_FOUND;
- }
- String currentLauncherPackageName = getCurrentLauncherPackageName(userId);
+ String currentLauncherPackageName = getCurrentLauncherPackageName(getParentUserId(userId));
if ((currentLauncherPackageName == null || !callerPackageName.equals(
currentLauncherPackageName)) && callingUid != Process.SHELL_UID) {
// TODO(b/311619990): Remove dependency on SHELL_UID for testing
@@ -316,6 +317,13 @@ public class PackageArchiver {
return START_ABORTED;
}
+ // Profiles share their UI and default apps, so we have to get the profile parent before
+ // fetching the default launcher.
+ private int getParentUserId(int userId) {
+ UserInfo profileParent = getUserManager().getProfileParent(userId);
+ return profileParent == null ? userId : profileParent.id;
+ }
+
/**
* Returns true if the componentName targeted by the intent corresponds to that of an archived
* app.
@@ -1128,6 +1136,13 @@ public class PackageArchiver {
return mAppOpsManager;
}
+ private UserManager getUserManager() {
+ if (mUserManager == null) {
+ mUserManager = mContext.getSystemService(UserManager.class);
+ }
+ return mUserManager;
+ }
+
private void storeArchiveState(String packageName, ArchiveState archiveState, int userId)
throws PackageManager.NameNotFoundException {
synchronized (mPm.mLock) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b505a7c1b743..7349755402b1 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import static android.content.Intent.ACTION_SCREEN_OFF;
import static android.content.Intent.ACTION_SCREEN_ON;
+import static android.content.Intent.EXTRA_USER_ID;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.UserManager.DEV_CREATE_OVERRIDE_PROPERTY;
@@ -25,6 +26,8 @@ import static android.os.UserManager.DISALLOW_USER_SWITCH;
import static android.os.UserManager.SYSTEM_USER_MODE_EMULATION_PROPERTY;
import static android.os.UserManager.USER_OPERATION_ERROR_UNKNOWN;
+import static com.android.internal.app.SetScreenLockDialogActivity.EXTRA_ORIGIN_USER_ID;
+import static com.android.internal.app.SetScreenLockDialogActivity.LAUNCH_REASON_DISABLE_QUIET_MODE;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_ABORTED;
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_UNSPECIFIED;
@@ -137,6 +140,7 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.SetScreenLockDialogActivity;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.RoSystemProperties;
@@ -1676,6 +1680,10 @@ public class UserManagerService extends IUserManager.Stub {
synchronized (mUsersLock) {
userInfo = getUserInfo(userId);
}
+ if (userInfo == null) {
+ throw new IllegalArgumentException("Invalid user. Can't find user details "
+ + "for userId " + userId);
+ }
if (!userInfo.isManagedProfile()) {
throw new IllegalArgumentException("Invalid flags: " + flags
+ ". Can't skip credential check for the user");
@@ -1692,6 +1700,19 @@ public class UserManagerService extends IUserManager.Stub {
if (onlyIfCredentialNotRequired) {
return false;
}
+
+ if (android.multiuser.Flags.showSetScreenLockDialog()) {
+ // Show the prompt to set a new screen lock if the device does not have one
+ final KeyguardManager km = mContext.getSystemService(KeyguardManager.class);
+ if (km != null && !km.isDeviceSecure()) {
+ Intent setScreenLockPromptIntent =
+ SetScreenLockDialogActivity
+ .createBaseIntent(LAUNCH_REASON_DISABLE_QUIET_MODE);
+ setScreenLockPromptIntent.putExtra(EXTRA_ORIGIN_USER_ID, userId);
+ mContext.startActivity(setScreenLockPromptIntent);
+ return false;
+ }
+ }
showConfirmCredentialToDisableQuietMode(userId, target, callingPackage);
return false;
}
@@ -1915,7 +1936,7 @@ public class UserManagerService extends IUserManager.Stub {
if (target != null) {
callBackIntent.putExtra(Intent.EXTRA_INTENT, target);
}
- callBackIntent.putExtra(Intent.EXTRA_USER_ID, userId);
+ callBackIntent.putExtra(EXTRA_USER_ID, userId);
callBackIntent.setPackage(mContext.getPackageName());
callBackIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callingPackage);
callBackIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 067a012ed373..114daaac3c18 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -306,6 +306,7 @@ public final class UserTypeFactory {
.setDarkThemeBadgeColors(
R.color.white)
.setDefaultRestrictions(getDefaultProfileRestrictions())
+ .setDefaultCrossProfileIntentFilters(getDefaultPrivateCrossProfileIntentFilter())
.setDefaultUserProperties(new UserProperties.Builder()
.setStartWithParent(true)
.setCredentialShareableWithParent(true)
@@ -446,6 +447,11 @@ public final class UserTypeFactory {
return DefaultCrossProfileIntentFiltersUtils.getDefaultCloneProfileFilters();
}
+ private static List<DefaultCrossProfileIntentFilter> getDefaultPrivateCrossProfileIntentFilter()
+ {
+ return DefaultCrossProfileIntentFiltersUtils.getDefaultPrivateProfileFilters();
+ }
+
/** Gets a default bundle, keyed by Settings.Secure String names, for non-managed profiles. */
private static Bundle getDefaultNonManagedProfileSecureSettings() {
final Bundle settings = new Bundle();
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index dd2b409c7100..1a9e012a7c53 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -47,6 +47,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.DataLoaderType;
+import android.content.pm.Flags;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
@@ -541,7 +542,12 @@ final class VerifyingSession {
}
final int verificationCodeAtTimeout;
- if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
+ // Allows package verification to continue in the event the app being updated is verifying
+ // itself and fails to respond
+ if (Flags.emergencyInstallPermission() && requiredVerifierPackages.contains(
+ pkgLite.packageName)) {
+ verificationCodeAtTimeout = PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT;
+ } else if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
verificationCodeAtTimeout = PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT;
} else {
verificationCodeAtTimeout = PackageManager.VERIFICATION_REJECT;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b5cd943ea605..8781bf19565e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -236,6 +236,7 @@ import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vibrator.HapticFeedbackVibrationProvider;
+import com.android.server.vibrator.VibratorFrameworkStatsLogger;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -6431,6 +6432,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
VibrationAttributes attrs =
mHapticFeedbackVibrationProvider.getVibrationAttributesForHapticFeedback(
effectId, /* bypassVibrationIntensitySetting= */ always);
+ VibratorFrameworkStatsLogger.logPerformHapticsFeedbackIfKeyboard(uid, effectId);
mVibrator.vibrate(uid, packageName, effect, reason, attrs);
return true;
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index af4da812d79e..bf06c2aba405 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -13854,7 +13854,9 @@ public class BatteryStatsImpl extends BatteryStats {
mNumAllUidCpuTimeReads += 2;
}
- updateSystemServerThreadStats();
+ if (!Flags.disableSystemServicePowerAttr()) {
+ updateSystemServerThreadStats();
+ }
if (powerAccumulator != null) {
updateCpuEnergyConsumerStatsLocked(cpuClusterChargeUC, powerAccumulator);
@@ -15428,17 +15430,18 @@ public class BatteryStatsImpl extends BatteryStats {
mPerDisplayBatteryStats[i].screenStateAtLastEnergyMeasurement = screenState;
}
- final boolean compatibleConfig;
if (supportedStandardBuckets != null) {
final EnergyConsumerStats.Config config = new EnergyConsumerStats.Config(
supportedStandardBuckets, customBucketNames,
SUPPORTED_PER_PROCESS_STATE_STANDARD_ENERGY_BUCKETS,
getBatteryConsumerProcessStateNames());
- if (mEnergyConsumerStatsConfig == null) {
- compatibleConfig = true;
- } else {
- compatibleConfig = mEnergyConsumerStatsConfig.isCompatible(config);
+ if (mEnergyConsumerStatsConfig != null
+ && !mEnergyConsumerStatsConfig.isCompatible(config)) {
+ // Supported power buckets changed since last boot.
+ // Existing data is no longer reliable.
+ resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(),
+ RESET_REASON_ENERGY_CONSUMER_BUCKETS_CHANGE);
}
mEnergyConsumerStatsConfig = config;
@@ -15454,18 +15457,14 @@ public class BatteryStatsImpl extends BatteryStats {
mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
}
} else {
- compatibleConfig = (mEnergyConsumerStatsConfig == null);
- // EnergyConsumer no longer supported, wipe out the existing data.
+ if (mEnergyConsumerStatsConfig != null) {
+ // EnergyConsumer no longer supported, wipe out the existing data.
+ resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(),
+ RESET_REASON_ENERGY_CONSUMER_BUCKETS_CHANGE);
+ }
mEnergyConsumerStatsConfig = null;
mGlobalEnergyConsumerStats = null;
}
-
- if (!compatibleConfig) {
- // Supported power buckets changed since last boot.
- // Existing data is no longer reliable.
- resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(),
- RESET_REASON_ENERGY_CONSUMER_BUCKETS_CHANGE);
- }
}
@GuardedBy("this")
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index 099c9ae33bfb..17f5e9585c22 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -44,6 +44,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
@@ -318,8 +319,12 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
if (SubscriptionManager.isValidSubscriptionId(subId)) {
// Get only configs as needed to save memory.
- final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId,
- VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
+ final PersistableBundle carrierConfig =
+ Flags.fixCrashOnGettingConfigWhenPhoneIsGone()
+ ? CarrierConfigManager.getCarrierConfigSubset(mContext, subId,
+ VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS)
+ : mCarrierConfigManager.getConfigForSubId(subId,
+ VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) {
mReadySubIdsBySlotId.put(slotId, subId);
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index 743d02d100b1..70e2e27a3bae 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -102,6 +102,23 @@ final class HalVibration extends Vibration {
}
/**
+ * Resolves the default vibration amplitude of {@link #getEffectToPlay()} and each fallback.
+ *
+ * @param defaultAmplitude An integer in [1,255] representing the device default amplitude to
+ * replace the {@link VibrationEffect#DEFAULT_AMPLITUDE}.
+ */
+ public void resolveEffects(int defaultAmplitude) {
+ CombinedVibration newEffect =
+ mEffectToPlay.transform(VibrationEffect::resolve, defaultAmplitude);
+ if (!Objects.equals(mEffectToPlay, newEffect)) {
+ mEffectToPlay = newEffect;
+ }
+ for (int i = 0; i < mFallbacks.size(); i++) {
+ mFallbacks.setValueAt(i, mFallbacks.valueAt(i).resolve(defaultAmplitude));
+ }
+ }
+
+ /**
* Scales the {@link #getEffectToPlay()} and each fallback effect with a scaling transformation.
*
* @param scaler A {@link VibrationEffect.Transformation<Integer>} that takes one of the
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index c9805c706fbc..7163319f281a 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -73,6 +73,13 @@ final class VibrationScaler {
}
/**
+ * Returns the default vibration amplitude configured for this device, value in [1,255].
+ */
+ public int getDefaultVibrationAmplitude() {
+ return mDefaultVibrationAmplitude;
+ }
+
+ /**
* Calculates the scale to be applied to external vibration with given usage.
*
* @param usageHint one of VibrationAttributes.USAGE_*
@@ -149,7 +156,7 @@ final class VibrationScaler {
&& mAdaptiveHapticsScales.size() > 0
&& mAdaptiveHapticsScales.contains(usageHint)) {
float adaptiveScale = mAdaptiveHapticsScales.get(usageHint);
- segment = segment.scale(adaptiveScale);
+ segment = segment.scaleLinearly(adaptiveScale);
}
segments.set(i, segment);
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index 9cf942e91439..f6af9ad991ff 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -160,7 +160,10 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
if (Flags.adaptiveHapticsEnabled()) {
waitForVibrationParamsIfRequired();
}
+ // Scale resolves the default amplitudes from the effect before scaling them.
mVibration.scaleEffects(mVibrationScaler::scale);
+ } else {
+ mVibration.resolveEffects(mVibrationScaler.getDefaultVibrationAmplitude());
}
mVibration.adaptToDevice(mDeviceAdapter);
diff --git a/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java b/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java
index f600a2964cbc..7e601b64ad18 100644
--- a/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java
+++ b/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java
@@ -19,10 +19,12 @@ package com.android.server.vibrator;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Slog;
+import android.view.HapticFeedbackConstants;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.modules.expresslog.Counter;
import java.util.ArrayDeque;
import java.util.Queue;
@@ -137,4 +139,21 @@ public class VibratorFrameworkStatsLogger {
mVibrationReportedLogIntervalMillis);
}
}
+
+ /** Logs only if the haptics feedback effect is one of the KEYBOARD_ constants. */
+ public static void logPerformHapticsFeedbackIfKeyboard(int uid, int hapticsFeedbackEffect) {
+ boolean isKeyboard;
+ switch (hapticsFeedbackEffect) {
+ case HapticFeedbackConstants.KEYBOARD_TAP:
+ case HapticFeedbackConstants.KEYBOARD_RELEASE:
+ isKeyboard = true;
+ break;
+ default:
+ isKeyboard = false;
+ break;
+ }
+ if (isKeyboard) {
+ Counter.logIncrementWithUid("vibrator.value_perform_haptic_feedback_keyboard", uid);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 5d172a9feef3..be5d15877e32 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -448,6 +448,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
VibrationAttributes attrs =
hapticVibrationProvider.getVibrationAttributesForHapticFeedback(
constant, /* bypassVibrationIntensitySetting= */ always);
+ VibratorFrameworkStatsLogger.logPerformHapticsFeedbackIfKeyboard(uid, constant);
return vibrateWithoutPermissionCheck(uid, deviceId, opPkg, combinedVibration, attrs,
"performHapticFeedback: " + reason, token);
}
@@ -883,8 +884,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private Vibration.EndInfo startVibrationOnInputDevicesLocked(HalVibration vib) {
if (!vib.callerInfo.attrs.isFlagSet(
VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)) {
- // Scale effect before dispatching it to the input devices.
+ // Scale resolves the default amplitudes from the effect before scaling them.
vib.scaleEffects(mVibrationScaler::scale);
+ } else {
+ vib.resolveEffects(mVibrationScaler.getDefaultVibrationAmplitude());
}
mInputDeviceDelegate.vibrateIfAvailable(vib.callerInfo, vib.getEffectToPlay());
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 764170a77f17..c3efcb14f223 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2228,8 +2228,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (wallpaper == null || !wallpaper.mSupportsMultiCrop) return null;
SparseArray<Rect> relativeSuggestedCrops =
mWallpaperCropper.getRelativeCropHints(wallpaper);
- Point croppedBitmapSize =
- new Point(wallpaper.cropHint.width(), wallpaper.cropHint.height());
+ Point croppedBitmapSize = new Point(
+ (int) (0.5f + wallpaper.cropHint.width() / wallpaper.mSampleSize),
+ (int) (0.5f + wallpaper.cropHint.height() / wallpaper.mSampleSize));
SparseArray<Rect> relativeDefaultCrops =
mWallpaperCropper.getDefaultCrops(relativeSuggestedCrops, croppedBitmapSize);
SparseArray<Rect> adjustedRelativeSuggestedCrops = new SparseArray<>();
diff --git a/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
index 3077fb8aaee9..e230b95fe907 100644
--- a/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
+++ b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
@@ -43,16 +43,16 @@ final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearable
com.android.server.wearable.RemoteWearableSensingService.class.getSimpleName();
private final static boolean DEBUG = false;
- private final Object mSecureWearableConnectionLock = new Object();
+ private final Object mSecureConnectionLock = new Object();
- // mNextSecureWearableConnectionContext will only be non-null when we are waiting for the
+ // mNextSecureConnectionContext will only be non-null when we are waiting for the
// WearableSensingService process to restart. It will be set to null after it is passed into
// WearableSensingService.
- @GuardedBy("mSecureWearableConnectionLock")
- private SecureWearableConnectionContext mNextSecureWearableConnectionContext;
+ @GuardedBy("mSecureConnectionLock")
+ private SecureWearableConnectionContext mNextSecureConnectionContext;
- @GuardedBy("mSecureWearableConnectionLock")
- private boolean mSecureWearableConnectionProvided = false;
+ @GuardedBy("mSecureConnectionLock")
+ private boolean mSecureConnectionProvided = false;
RemoteWearableSensingService(Context context, ComponentName serviceName,
int userId) {
@@ -77,23 +77,23 @@ final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearable
* @param secureWearableConnection The secure connection to the wearable
* @param callback The callback for service status
*/
- public void provideSecureWearableConnection(
+ public void provideSecureConnection(
ParcelFileDescriptor secureWearableConnection, RemoteCallback callback) {
if (DEBUG) {
- Slog.i(TAG, "#provideSecureWearableConnection");
+ Slog.i(TAG, "#provideSecureConnection");
}
if (!Flags.enableRestartWssProcess()) {
Slog.d(
TAG,
"FLAG_ENABLE_RESTART_WSS_PROCESS is disabled. Do not attempt to restart the"
+ " WearableSensingService process");
- provideSecureWearableConnectionInternal(secureWearableConnection, callback);
+ provideSecureConnectionInternal(secureWearableConnection, callback);
return;
}
- synchronized (mSecureWearableConnectionLock) {
- if (mNextSecureWearableConnectionContext != null) {
+ synchronized (mSecureConnectionLock) {
+ if (mNextSecureConnectionContext != null) {
// A process restart is in progress, #binderDied is about to be called. Replace
- // the previous mNextSecureWearableConnectionContext with the current one
+ // the previous mNextSecureConnectionContext with the current one
Slog.i(
TAG,
"A new wearable connection is provided before the process restart triggered"
@@ -101,33 +101,33 @@ final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearable
+ " connection.");
if (Flags.enableProvideWearableConnectionApi()) {
WearableSensingManagerPerUserService.notifyStatusCallback(
- mNextSecureWearableConnectionContext.mStatusCallback,
+ mNextSecureConnectionContext.mStatusCallback,
WearableSensingManager.STATUS_CHANNEL_ERROR);
}
- mNextSecureWearableConnectionContext =
+ mNextSecureConnectionContext =
new SecureWearableConnectionContext(secureWearableConnection, callback);
return;
}
- if (!mSecureWearableConnectionProvided) {
+ if (!mSecureConnectionProvided) {
// no need to kill the process
- provideSecureWearableConnectionInternal(secureWearableConnection, callback);
- mSecureWearableConnectionProvided = true;
+ provideSecureConnectionInternal(secureWearableConnection, callback);
+ mSecureConnectionProvided = true;
return;
}
- mNextSecureWearableConnectionContext =
+ mNextSecureConnectionContext =
new SecureWearableConnectionContext(secureWearableConnection, callback);
// Killing the process causes the binder to die. #binderDied will then be triggered
killWearableSensingServiceProcess();
}
}
- private void provideSecureWearableConnectionInternal(
+ private void provideSecureConnectionInternal(
ParcelFileDescriptor secureWearableConnection, RemoteCallback callback) {
Slog.d(TAG, "Providing secure wearable connection.");
var unused =
post(
service -> {
- service.provideSecureWearableConnection(
+ service.provideSecureConnection(
secureWearableConnection, callback);
try {
// close the local fd after it has been sent to the WSS process
@@ -141,15 +141,15 @@ final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearable
@Override
public void binderDied() {
super.binderDied();
- synchronized (mSecureWearableConnectionLock) {
- if (mNextSecureWearableConnectionContext != null) {
+ synchronized (mSecureConnectionLock) {
+ if (mNextSecureConnectionContext != null) {
// This will call #post, which will recreate the process and bind to it
- provideSecureWearableConnectionInternal(
- mNextSecureWearableConnectionContext.mSecureWearableConnection,
- mNextSecureWearableConnectionContext.mStatusCallback);
- mNextSecureWearableConnectionContext = null;
+ provideSecureConnectionInternal(
+ mNextSecureConnectionContext.mSecureConnection,
+ mNextSecureConnectionContext.mStatusCallback);
+ mNextSecureConnectionContext = null;
} else {
- mSecureWearableConnectionProvided = false;
+ mSecureConnectionProvided = false;
Slog.w(TAG, "Binder died but there is no secure wearable connection to provide.");
}
}
@@ -307,12 +307,12 @@ final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearable
}
private static class SecureWearableConnectionContext {
- final ParcelFileDescriptor mSecureWearableConnection;
+ final ParcelFileDescriptor mSecureConnection;
final RemoteCallback mStatusCallback;
SecureWearableConnectionContext(
ParcelFileDescriptor secureWearableConnection, RemoteCallback statusCallback) {
- this.mSecureWearableConnection = secureWearableConnection;
+ this.mSecureConnection = secureWearableConnection;
this.mStatusCallback = statusCallback;
}
}
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
index 2b43203628d9..34b9fe968994 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
@@ -189,9 +189,9 @@ final class WearableSensingManagerPerUserService extends
* Creates a CompanionDeviceManager secure channel and sends a proxy to the wearable sensing
* service.
*/
- public void onProvideWearableConnection(
+ public void onProvideConnection(
ParcelFileDescriptor wearableConnection, RemoteCallback callback) {
- Slog.i(TAG, "onProvideWearableConnection in per user service.");
+ Slog.i(TAG, "onProvideConnection in per user service.");
synchronized (mLock) {
if (!setUpServiceIfNeeded()) {
Slog.w(TAG, "Detection service is not available at this moment.");
@@ -217,7 +217,7 @@ final class WearableSensingManagerPerUserService extends
Slog.i(TAG, "calling over to remote service.");
synchronized (mLock) {
ensureRemoteServiceInitiated();
- mRemoteService.provideSecureWearableConnection(
+ mRemoteService.provideSecureConnection(
secureTransport, callback);
}
}
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
index d05482d75083..5f6ffd988c84 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
@@ -399,9 +399,9 @@ public class WearableSensingManagerService extends
private final class WearableSensingManagerInternal extends IWearableSensingManager.Stub {
@Override
- public void provideWearableConnection(
+ public void provideConnection(
ParcelFileDescriptor wearableConnection, RemoteCallback callback) {
- Slog.i(TAG, "WearableSensingManagerInternal provideWearableConnection.");
+ Slog.i(TAG, "WearableSensingManagerInternal provideConnection.");
Objects.requireNonNull(wearableConnection);
Objects.requireNonNull(callback);
mContext.enforceCallingOrSelfPermission(
@@ -413,7 +413,7 @@ public class WearableSensingManagerService extends
return;
}
callPerUserServiceIfExist(
- service -> service.onProvideWearableConnection(wearableConnection, callback),
+ service -> service.onProvideConnection(wearableConnection, callback),
callback);
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 7ccc250a5bfd..44a0547a6828 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1543,8 +1543,6 @@ final class AccessibilityController {
private final Set<IBinder> mTempBinderSet = new ArraySet<>();
- private final Point mTempPoint = new Point();
-
private final Region mTempRegion = new Region();
private final Region mTempRegion2 = new Region();
@@ -1614,8 +1612,9 @@ final class AccessibilityController {
Slog.i(LOG_TAG, "computeChangedWindows()");
}
- final List<WindowInfo> windows;
+ List<WindowInfo> windows = null;
final List<AccessibilityWindow> visibleWindows = new ArrayList<>();
+ final Point screenSize = new Point();
final int topFocusedDisplayId;
IBinder topFocusedWindowToken = null;
@@ -1643,19 +1642,27 @@ final class AccessibilityController {
return;
}
final Display display = dc.getDisplay();
- display.getRealSize(mTempPoint);
+ display.getRealSize(screenSize);
mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked(
mDisplayId, visibleWindows);
- windows = buildWindowInfoListLocked(visibleWindows, mTempPoint);
+ if (!com.android.server.accessibility.Flags.computeWindowChangesOnA11y()) {
+ windows = buildWindowInfoListLocked(visibleWindows, screenSize);
+ }
// Gets the top focused display Id and window token for supporting multi-display.
topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId();
topFocusedWindowToken = topFocusedWindowState.mClient.asBinder();
}
- mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
- topFocusedWindowToken, windows);
+
+ if (com.android.server.accessibility.Flags.computeWindowChangesOnA11y()) {
+ mCallback.onAccessibilityWindowsChanged(forceSend, topFocusedDisplayId,
+ topFocusedWindowToken, screenSize, visibleWindows);
+ } else {
+ mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
+ topFocusedWindowToken, windows);
+ }
// Recycle the windows as we do not need them.
for (final AccessibilityWindowsPopulator.AccessibilityWindow window : visibleWindows) {
@@ -1664,6 +1671,9 @@ final class AccessibilityController {
mInitialized = true;
}
+ // Here are old code paths, called when computeWindowChangesOnA11y flag is disabled.
+ // LINT.IfChange
+
/**
* From a list of windows, decides windows to be exposed to accessibility based on touchable
* region in the screen.
@@ -1823,6 +1833,8 @@ final class AccessibilityController {
&& windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
}
+ // LINT.ThenChange(/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java)
+
private WindowState getTopFocusWindow() {
return mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus;
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index 3cf19ddbd89d..ac3251c9bb12 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -22,6 +22,7 @@ import static com.android.internal.util.DumpUtils.dumpSparseArray;
import static com.android.server.wm.utils.RegionUtils.forEachRect;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -655,6 +656,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
private final Region mTouchableRegionInScreen = new Region();
private final Region mTouchableRegionInWindow = new Region();
private WindowInfo mWindowInfo;
+ private Rect mSystemBarInsetFrame = null;
/**
@@ -718,6 +720,16 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
Slog.w(TAG, "can't find spec");
}
}
+
+ // Compute system bar insets frame if needed.
+ if (com.android.server.accessibility.Flags.computeWindowChangesOnA11y()
+ && windowState != null && instance.isUntouchableNavigationBar()) {
+ final InsetsSourceProvider provider =
+ windowState.getControllableInsetProvider();
+ if (provider != null) {
+ instance.mSystemBarInsetFrame = provider.getSource().getFrame();
+ }
+ }
return instance;
}
@@ -812,6 +824,15 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
return mIsPIPMenu;
}
+ /**
+ * @return the system inset frame size if the window is untouchable navigation bar.
+ * Returns null otherwise.
+ */
+ @Nullable
+ public Rect getSystemBarInsetsFrame() {
+ return mSystemBarInsetFrame;
+ }
+
private static void getTouchableRegionInWindow(boolean shouldMagnify, Region inRegion,
Region outRegion, Rect frame, Matrix inverseMatrix, Matrix displayMatrix) {
// Some modal windows, like the activity with Theme.dialog, has the full screen
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 56024f75545c..2ef34a8c3914 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1535,11 +1535,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Picture-in-picture mode changes also trigger a multi-window mode change as well, so
// update that here in order. Set the last reported MW state to the same as the PiP
// state since we haven't yet actually resized the task (these callbacks need to
- // precede the configuration change from the resize.
+ // precede the configuration change from the resize.)
mLastReportedPictureInPictureMode = inPictureInPictureMode;
mLastReportedMultiWindowMode = inPictureInPictureMode;
ensureActivityConfiguration(true /* ignoreVisibility */);
- if (inPictureInPictureMode && findMainWindow() == null) {
+ if (inPictureInPictureMode && findMainWindow() == null
+ && task.topRunningActivity() == this) {
// Prevent malicious app entering PiP without valid WindowState, which can in turn
// result a non-touchable PiP window since the InputConsumer for PiP requires it.
EventLog.writeEvent(0x534e4554, "265293293", -1, "");
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 0c5c8ae2b628..0e401ebc94b5 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -562,11 +562,14 @@ public class ActivityStartController {
@Nullable IBinder errorCallbackToken) {
final ActivityRecord caller =
resultTo != null ? ActivityRecord.forTokenLocked(resultTo) : null;
+ final String resolvedType =
+ activityIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
return obtainStarter(activityIntent, "startActivityInTaskFragment")
.setActivityOptions(activityOptions)
.setInTaskFragment(taskFragment)
.setResultTo(resultTo)
.setRequestCode(-1)
+ .setResolvedType(resolvedType)
.setCallingUid(callingUid)
.setCallingPid(callingPid)
.setRealCallingUid(callingUid)
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 63cdf0ef4b43..066d26222fbd 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -414,6 +414,7 @@ class ActivityStarter {
int filterCallingUid;
PendingIntentRecord originatingPendingIntent;
BackgroundStartPrivileges forcedBalByPiSender;
+ boolean freezeScreen;
final StringBuilder logMessage = new StringBuilder();
@@ -477,6 +478,7 @@ class ActivityStarter {
filterCallingUid = UserHandle.USER_NULL;
originatingPendingIntent = null;
forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ freezeScreen = false;
errorCallbackToken = null;
}
@@ -520,6 +522,7 @@ class ActivityStarter {
filterCallingUid = request.filterCallingUid;
originatingPendingIntent = request.originatingPendingIntent;
forcedBalByPiSender = request.forcedBalByPiSender;
+ freezeScreen = request.freezeScreen;
errorCallbackToken = request.errorCallbackToken;
}
@@ -1525,6 +1528,18 @@ class ActivityStarter {
Transition newTransition = transitionController.isShellTransitionsEnabled()
? transitionController.createAndStartCollecting(TRANSIT_OPEN) : null;
RemoteTransition remoteTransition = r.takeRemoteTransition();
+ // Create a display snapshot as soon as possible.
+ if (newTransition != null && mRequest.freezeScreen) {
+ final TaskDisplayArea tda = mLaunchParams.hasPreferredTaskDisplayArea()
+ ? mLaunchParams.mPreferredTaskDisplayArea
+ : mRootWindowContainer.getDefaultTaskDisplayArea();
+ final DisplayContent dc = mRootWindowContainer.getDisplayContentOrCreate(
+ tda.getDisplayId());
+ if (dc != null) {
+ transitionController.collect(dc);
+ transitionController.collectVisibleChange(dc);
+ }
+ }
try {
mService.deferWindowLayout();
transitionController.collect(r);
@@ -3270,6 +3285,11 @@ class ActivityStarter {
return this;
}
+ ActivityStarter setFreezeScreen(boolean freezeScreen) {
+ mRequest.freezeScreen = freezeScreen;
+ return this;
+ }
+
ActivityStarter setErrorCallbackToken(@Nullable IBinder errorCallbackToken) {
mRequest.errorCallbackToken = errorCallbackToken;
return this;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index ed556a55010e..4a5b2211800c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -275,6 +275,19 @@ public abstract class ActivityTaskManagerInternal {
int startFlags, @Nullable Bundle options, int userId);
/**
+ * Start activity {@code intent} with initially under screenshot. The screen of launching
+ * display will be frozen before transition occur.
+ *
+ * - DO NOT call it with the calling UID cleared.
+ * - The caller must do the calling user ID check.
+ *
+ * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
+ */
+ public abstract int startActivityWithScreenshot(@NonNull Intent intent,
+ @NonNull String callingPackage, int callingUid, int callingPid,
+ @Nullable IBinder resultTo, @Nullable Bundle options, int userId);
+
+ /**
* Called after virtual display Id is updated by
* {@link com.android.server.vr.Vr2dDisplay} with a specific
* {@param vr2dDisplayId}.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ec3c1224cb59..24c8ebbfc2f7 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -102,6 +102,9 @@ import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_FIRST_O
import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_LAST_ORDERED_ID;
import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_FIRST_ORDERED_ID;
import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_LAST_ORDERED_ID;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
@@ -1134,22 +1137,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
* Return the global configuration used by the process corresponding to the input pid. This is
* usually the global configuration with some overrides specific to that process.
*/
- Configuration getGlobalConfigurationForCallingPid() {
+ private Configuration getGlobalConfigurationForCallingPid() {
final int pid = Binder.getCallingPid();
- return getGlobalConfigurationForPid(pid);
- }
-
- /**
- * Return the global configuration used by the process corresponding to the given pid.
- */
- Configuration getGlobalConfigurationForPid(int pid) {
if (pid == MY_PID || pid < 0) {
return getGlobalConfiguration();
}
- synchronized (mGlobalLock) {
- final WindowProcessController app = mProcessMap.getProcess(pid);
- return app != null ? app.getConfiguration() : getGlobalConfiguration();
- }
+ final WindowProcessController app = mProcessMap.getProcess(pid);
+ return app != null ? app.getConfiguration() : getGlobalConfiguration();
}
/**
@@ -4173,7 +4167,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
task = mRootWindowContainer.getDefaultTaskDisplayArea().getRootTask(
t -> t.isActivityTypeStandard());
}
- if (task != null && task.getTopMostActivity() != null) {
+ if (task != null && task.getTopMostActivity() != null
+ && !task.getTopMostActivity().isState(FINISHING, DESTROYING, DESTROYED)) {
mWindowManager.mAtmService.mActivityClientController.onPictureInPictureUiStateChanged(
task.getTopMostActivity(), pipState);
}
@@ -6019,6 +6014,24 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
false /*validateIncomingUser*/);
}
+ @Override
+ public int startActivityWithScreenshot(@NonNull Intent intent,
+ @NonNull String callingPackage, int callingUid, int callingPid,
+ @Nullable IBinder resultTo, @Nullable Bundle options, int userId) {
+ return getActivityStartController()
+ .obtainStarter(intent, "startActivityWithScreenshot")
+ .setCallingUid(callingUid)
+ .setCallingPid(callingPid)
+ .setCallingPackage(callingPackage)
+ .setResultTo(resultTo)
+ .setActivityOptions(createSafeActivityOptionsWithBalAllowed(options))
+ .setRealCallingUid(Binder.getCallingUid())
+ .setUserId(userId)
+ .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+ .setFreezeScreen(true)
+ .execute();
+ }
+
/**
* Called after virtual display Id is updated by
* {@link com.android.server.vr.Vr2dDisplay} with a specific
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index a3e28693cb21..b68e67e291e7 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -25,6 +25,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACT
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
+import android.app.ActivityManager;
import android.content.ClipData;
import android.content.Context;
import android.hardware.input.InputManagerGlobal;
@@ -43,8 +44,8 @@ import android.view.PointerIcon;
import android.view.SurfaceControl;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
+import android.window.IGlobalDragListener;
import android.window.IUnhandledDragCallback;
-import android.window.IUnhandledDragListener;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
@@ -81,9 +82,9 @@ class DragDropController {
private WindowManagerService mService;
private final Handler mHandler;
- // The unhandled drag listener for handling cross-window drags that end with no target window
- private IUnhandledDragListener mUnhandledDragListener;
- private final IBinder.DeathRecipient mUnhandledDragListenerDeathRecipient =
+ // The global drag listener for handling cross-window drags
+ private IGlobalDragListener mGlobalDragListener;
+ private final IBinder.DeathRecipient mGlobalDragListenerDeathRecipient =
new IBinder.DeathRecipient() {
@Override
public void binderDied() {
@@ -91,7 +92,7 @@ class DragDropController {
if (hasPendingUnhandledDropCallback()) {
onUnhandledDropCallback(false /* consumedByListeners */);
}
- setUnhandledDragListener(null);
+ setGlobalDragListener(null);
}
}
};
@@ -129,29 +130,22 @@ class DragDropController {
/**
* Sets the listener for unhandled cross-window drags.
*/
- public void setUnhandledDragListener(IUnhandledDragListener listener) {
- if (mUnhandledDragListener != null && mUnhandledDragListener.asBinder() != null) {
- mUnhandledDragListener.asBinder().unlinkToDeath(
- mUnhandledDragListenerDeathRecipient, 0);
+ public void setGlobalDragListener(IGlobalDragListener listener) {
+ if (mGlobalDragListener != null && mGlobalDragListener.asBinder() != null) {
+ mGlobalDragListener.asBinder().unlinkToDeath(
+ mGlobalDragListenerDeathRecipient, 0);
}
- mUnhandledDragListener = listener;
+ mGlobalDragListener = listener;
if (listener != null && listener.asBinder() != null) {
try {
- mUnhandledDragListener.asBinder().linkToDeath(
- mUnhandledDragListenerDeathRecipient, 0);
+ mGlobalDragListener.asBinder().linkToDeath(
+ mGlobalDragListenerDeathRecipient, 0);
} catch (RemoteException e) {
- mUnhandledDragListener = null;
+ mGlobalDragListener = null;
}
}
}
- /**
- * Returns whether there is an unhandled drag listener set.
- */
- boolean hasUnhandledDragListener() {
- return mUnhandledDragListener != null;
- }
-
void sendDragStartedIfNeededLocked(WindowState window) {
mDragState.sendDragStartedIfNeededLocked(window);
}
@@ -351,7 +345,20 @@ class DragDropController {
final boolean relinquishDragSurfaceToDropTarget =
consumed && mDragState.targetInterceptsGlobalDrag(callingWin);
+ final boolean isCrossWindowDrag = !mDragState.mLocalWin.equals(token);
mDragState.endDragLocked(consumed, relinquishDragSurfaceToDropTarget);
+
+ final Task droppedWindowTask = callingWin.getTask();
+ if (com.android.window.flags.Flags.delegateUnhandledDrags()
+ && mGlobalDragListener != null && droppedWindowTask != null && consumed
+ && isCrossWindowDrag) {
+ try {
+ mGlobalDragListener.onCrossWindowDrop(droppedWindowTask.getTaskInfo());
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Failed to call global drag listener for cross-window "
+ + "drop", e);
+ }
+ }
}
} finally {
mCallback.get().postReportDropResult();
@@ -367,19 +374,19 @@ class DragDropController {
final boolean isLocalDrag =
(mDragState.mFlags & (DRAG_FLAG_GLOBAL_SAME_APPLICATION | DRAG_FLAG_GLOBAL)) == 0;
if (!com.android.window.flags.Flags.delegateUnhandledDrags()
- || mUnhandledDragListener == null
+ || mGlobalDragListener == null
|| isLocalDrag) {
// Skip if the flag is disabled, there is no unhandled-drag listener, or if this is a
// purely local drag
if (DEBUG_DRAG) Slog.d(TAG_WM, "Skipping unhandled listener "
- + "(listener=" + mUnhandledDragListener + ", flags=" + mDragState.mFlags + ")");
+ + "(listener=" + mGlobalDragListener + ", flags=" + mDragState.mFlags + ")");
return false;
}
if (DEBUG_DRAG) Slog.d(TAG_WM, "Sending DROP to unhandled listener (" + reason + ")");
try {
// Schedule timeout for the unhandled drag listener to call back
sendTimeoutMessage(MSG_UNHANDLED_DROP_LISTENER_TIMEOUT, null, DRAG_TIMEOUT_MS);
- mUnhandledDragListener.onUnhandledDrop(dropEvent, new IUnhandledDragCallback.Stub() {
+ mGlobalDragListener.onUnhandledDrop(dropEvent, new IUnhandledDragCallback.Stub() {
@Override
public void notifyUnhandledDropComplete(boolean consumedByListener) {
if (DEBUG_DRAG) Slog.d(TAG_WM, "Unhandled listener finished handling DROP");
@@ -390,7 +397,7 @@ class DragDropController {
});
return true;
} catch (RemoteException e) {
- Slog.e(TAG_WM, "Failed to call unhandled drag listener", e);
+ Slog.e(TAG_WM, "Failed to call global drag listener for unhandled drop", e);
return false;
}
}
diff --git a/services/core/java/com/android/server/wm/SensitiveContentPackages.java b/services/core/java/com/android/server/wm/SensitiveContentPackages.java
index 5fe48d12d498..9ab11b8059a7 100644
--- a/services/core/java/com/android/server/wm/SensitiveContentPackages.java
+++ b/services/core/java/com/android/server/wm/SensitiveContentPackages.java
@@ -16,9 +16,16 @@
package com.android.server.wm;
+import static android.permission.flags.Flags.sensitiveNotificationAppProtection;
+import static android.view.flags.Flags.sensitiveContentAppProtection;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
import android.util.ArraySet;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.PrintWriter;
import java.util.Objects;
@@ -29,12 +36,26 @@ import java.util.Objects;
public class SensitiveContentPackages {
private final ArraySet<PackageInfo> mProtectedPackages = new ArraySet<>();
- /** Returns {@code true} if package/uid pair should be blocked from screen capture */
- public boolean shouldBlockScreenCaptureForApp(String pkg, int uid) {
+ /**
+ * Returns {@code true} if package/uid/window combination should be blocked
+ * from screen capture.
+ */
+ public boolean shouldBlockScreenCaptureForApp(String pkg, int uid, IBinder windowToken) {
+ if (!(sensitiveContentAppProtection() || sensitiveNotificationAppProtection())) {
+ return false;
+ }
+
for (int i = 0; i < mProtectedPackages.size(); i++) {
PackageInfo info = mProtectedPackages.valueAt(i);
if (info != null && info.mPkg.equals(pkg) && info.mUid == uid) {
- return true;
+ // sensitiveContentAppProtection blocks specific window where sensitive content
+ // is rendered, whereas sensitiveNotificationAppProtection blocks the package
+ // if the package has a sensitive notification.
+ if ((sensitiveContentAppProtection() && windowToken == info.getWindowToken())
+ || (sensitiveNotificationAppProtection() && info.getWindowToken() == null)
+ ) {
+ return true;
+ }
}
}
return false;
@@ -73,31 +94,50 @@ public class SensitiveContentPackages {
*/
public boolean clearBlockedApps() {
if (mProtectedPackages.isEmpty()) {
- // set was already empty
return false;
}
mProtectedPackages.clear();
return true;
}
+ /**
+ * @return the size of protected packages.
+ */
+ @VisibleForTesting
+ public int size() {
+ return mProtectedPackages.size();
+ }
+
void dump(PrintWriter pw) {
final String innerPrefix = " ";
pw.println("SensitiveContentPackages:");
pw.println(innerPrefix + "Packages that should block screen capture ("
+ mProtectedPackages.size() + "):");
for (PackageInfo info : mProtectedPackages) {
- pw.println(innerPrefix + " package=" + info.mPkg + " uid=" + info.mUid);
+ pw.println(innerPrefix + " package=" + info.mPkg + " uid=" + info.mUid
+ + " windowToken=" + info.mWindowToken);
}
}
- /** Helper class that represents a package/uid pair */
+ /**
+ * Helper class that represents a package, uid, and window token combination, window token
+ * is set to block screen capture at window level.
+ */
public static class PackageInfo {
- private String mPkg;
- private int mUid;
+ private final String mPkg;
+ private final int mUid;
+
+ @Nullable
+ private final IBinder mWindowToken;
public PackageInfo(String pkg, int uid) {
+ this(pkg, uid, null);
+ }
+
+ public PackageInfo(String pkg, int uid, IBinder windowToken) {
this.mPkg = pkg;
this.mUid = uid;
+ this.mWindowToken = windowToken;
}
@Override
@@ -105,12 +145,30 @@ public class SensitiveContentPackages {
if (this == o) return true;
if (!(o instanceof PackageInfo)) return false;
PackageInfo that = (PackageInfo) o;
- return mUid == that.mUid && Objects.equals(mPkg, that.mPkg);
+ return mUid == that.mUid && Objects.equals(mPkg, that.mPkg)
+ && Objects.equals(mWindowToken, that.mWindowToken);
}
@Override
public int hashCode() {
- return Objects.hash(mPkg, mUid);
+ return Objects.hash(mPkg, mUid, mWindowToken);
+ }
+
+ public IBinder getWindowToken() {
+ return mWindowToken;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public String getPkg() {
+ return mPkg;
+ }
+
+ @Override
+ public String toString() {
+ return "package=" + mPkg + " uid=" + mUid + " windowToken=" + mWindowToken;
}
}
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 10cbc6633533..85d81c4db2ca 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -104,6 +104,7 @@ import android.window.TaskFragmentOrganizerToken;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.ToBooleanFunction;
import com.android.server.am.HostingRecord;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.window.flags.Flags;
@@ -3025,11 +3026,17 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return false;
}
- // boost if there's an Activity window that has FLAG_DIM_BEHIND flag.
- return forAllWindows(
+ ToBooleanFunction<WindowState> getDimBehindWindow =
(w) -> (w.mAttrs.flags & FLAG_DIM_BEHIND) != 0 && w.mActivityRecord != null
&& w.mActivityRecord.isEmbedded() && (w.mActivityRecord.isVisibleRequested()
- || w.mActivityRecord.isVisible()), true);
+ || w.mActivityRecord.isVisible());
+ if (adjacentTf.forAllWindows(getDimBehindWindow, true)) {
+ // early return if the adjacent Tf has a dimming window.
+ return false;
+ }
+
+ // boost if there's an Activity window that has FLAG_DIM_BEHIND flag.
+ return forAllWindows(getDimBehindWindow, true);
}
@Override
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 5d9c42d4c3a8..7edc3a2b9786 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -691,12 +691,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
recordDisplay(wc.getDisplayContent());
if (info.mShowWallpaper) {
// Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
- final List<WindowState> wallpapers =
- wc.getDisplayContent().mWallpaperController.getAllTopWallpapers();
- for (int i = wallpapers.size() - 1; i >= 0; i--) {
- WindowState wallpaper = wallpapers.get(i);
- collect(wallpaper.mToken);
- }
+ wc.mDisplayContent.mWallpaperController.collectTopWallpapers(this);
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 6949a874b533..001f46d4c36c 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WALLPAPER;
@@ -55,7 +54,6 @@ import android.view.Display;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.WindowManager;
-import android.view.animation.Animation;
import android.window.ScreenCapture;
import com.android.internal.R;
@@ -80,6 +78,7 @@ class WallpaperController {
private WallpaperCropUtils mWallpaperCropUtils = null;
private DisplayContent mDisplayContent;
+ // Larger index has higher z-order.
private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>();
// If non-null, this is the currently visible window that is associated
@@ -126,18 +125,6 @@ class WallpaperController {
*/
private volatile boolean mIsWallpaperNotifiedOnDisplaySwitch;
- private final Consumer<WindowState> mFindWallpapers = w -> {
- if (w.mAttrs.type == TYPE_WALLPAPER) {
- WallpaperWindowToken token = w.mToken.asWallpaperToken();
- if (token.canShowWhenLocked() && !mFindResults.hasTopShowWhenLockedWallpaper()) {
- mFindResults.setTopShowWhenLockedWallpaper(w);
- } else if (!token.canShowWhenLocked()
- && !mFindResults.hasTopHideWhenLockedWallpaper()) {
- mFindResults.setTopHideWhenLockedWallpaper(w);
- }
- }
- };
-
private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> {
final boolean useShellTransition = w.mTransitionController.isShellTransitionsEnabled();
if (!useShellTransition) {
@@ -321,16 +308,6 @@ class WallpaperController {
return false;
}
- /**
- * Starts {@param a} on all wallpaper windows.
- */
- void startWallpaperAnimation(Animation a) {
- for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
- final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
- token.startAnimation(a);
- }
- }
-
boolean isWallpaperTargetAnimating() {
return mWallpaperTarget != null && mWallpaperTarget.isAnimating(TRANSITION | PARENTS)
&& (mWallpaperTarget.mActivityRecord == null
@@ -621,7 +598,8 @@ class WallpaperController {
if (Float.compare(window.mWallpaperZoomOut, zoom) != 0) {
window.mWallpaperZoomOut = zoom;
computeLastWallpaperZoomOut();
- for (WallpaperWindowToken token : mWallpaperTokens) {
+ for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
+ final WallpaperWindowToken token = mWallpaperTokens.get(i);
token.updateWallpaperOffset(false);
}
}
@@ -744,7 +722,7 @@ class WallpaperController {
mFindResults.setUseTopWallpaperAsTarget(true);
}
- mDisplayContent.forAllWindows(mFindWallpapers, true /* traverseTopToBottom */);
+ findWallpapers();
mDisplayContent.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */);
if (mFindResults.mNeedsShowWhenLockedWallpaper) {
// Keep wallpaper visible if the show-when-locked activities doesn't fill screen.
@@ -757,15 +735,29 @@ class WallpaperController {
}
}
- List<WindowState> getAllTopWallpapers() {
- ArrayList<WindowState> wallpapers = new ArrayList<>(2);
+ private void findWallpapers() {
+ for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
+ final WallpaperWindowToken token = mWallpaperTokens.get(i);
+ final boolean canShowWhenLocked = token.canShowWhenLocked();
+ for (int j = token.getChildCount() - 1; j >= 0; j--) {
+ final WindowState w = token.getChildAt(j);
+ if (!w.mIsWallpaper) continue;
+ if (canShowWhenLocked && !mFindResults.hasTopShowWhenLockedWallpaper()) {
+ mFindResults.setTopShowWhenLockedWallpaper(w);
+ } else if (!canShowWhenLocked && !mFindResults.hasTopHideWhenLockedWallpaper()) {
+ mFindResults.setTopHideWhenLockedWallpaper(w);
+ }
+ }
+ }
+ }
+
+ void collectTopWallpapers(Transition transition) {
if (mFindResults.hasTopShowWhenLockedWallpaper()) {
- wallpapers.add(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper);
+ transition.collect(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper);
}
if (mFindResults.hasTopHideWhenLockedWallpaper()) {
- wallpapers.add(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper);
+ transition.collect(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper);
}
- return wallpapers;
}
private boolean isFullscreen(WindowManager.LayoutParams attrs) {
@@ -1016,6 +1008,12 @@ class WallpaperController {
mWallpaperTokens.remove(token);
}
+ void onWallpaperTokenReordered() {
+ if (mWallpaperTokens.size() > 1) {
+ mWallpaperTokens.sort(null /* by WindowContainer#compareTo */);
+ }
+ }
+
@VisibleForTesting
boolean canScreenshotWallpaper() {
return canScreenshotWallpaper(getTopVisibleWallpaper());
@@ -1160,7 +1158,8 @@ class WallpaperController {
pw.print(prefix); pw.print("mPrevWallpaperTarget="); pw.println(mPrevWallpaperTarget);
}
- for (WallpaperWindowToken t : mWallpaperTokens) {
+ for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
+ final WallpaperWindowToken t = mWallpaperTokens.get(i);
pw.print(prefix); pw.println("token " + t + ":");
pw.print(prefix); pw.print(" canShowWhenLocked="); pw.println(t.canShowWhenLocked());
dumpValue(pw, prefix, "mWallpaperX", t.mWallpaperX);
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 1bcd882b5d64..dc500a2748cf 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -30,7 +30,6 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.SparseArray;
-import android.view.animation.Animation;
import com.android.internal.protolog.common.ProtoLog;
@@ -90,16 +89,14 @@ class WallpaperWindowToken extends WindowToken {
return;
}
mShowWhenLocked = showWhenLocked;
- // Move the window token to the front (private) or back (showWhenLocked). This is
- // possible
- // because the DisplayArea underneath TaskDisplayArea only contains TYPE_WALLPAPER
- // windows.
+ // Move the window token to the front (private) or back (showWhenLocked). This is possible
+ // because the DisplayArea underneath TaskDisplayArea only contains TYPE_WALLPAPER windows.
final int position = showWhenLocked ? POSITION_BOTTOM : POSITION_TOP;
- // Note: Moving all the way to the front or back breaks ordering based on addition
- // times.
- // We should never have more than one non-animating token of each type.
+ // Note: Moving all the way to the front or back breaks ordering based on addition times.
+ // There should never have more than one non-animating token of each type.
getParent().positionChildAt(position, this /* child */, false /*includingParents */);
+ mDisplayContent.mWallpaperController.onWallpaperTokenReordered();
}
boolean canShowWhenLocked() {
@@ -139,16 +136,6 @@ class WallpaperWindowToken extends WindowToken {
}
}
- /**
- * Starts {@param anim} on all children.
- */
- void startAnimation(Animation anim) {
- for (int ndx = mChildren.size() - 1; ndx >= 0; ndx--) {
- final WindowState windowState = mChildren.get(ndx);
- windowState.startAnimation(anim);
- }
- }
-
void updateWallpaperWindows(boolean visible) {
if (mVisibleRequested != visible) {
ProtoLog.d(WM_DEBUG_WALLPAPER, "Wallpaper token %s visible=%b",
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index f3bb37393e99..4698b6b2925c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -25,6 +25,7 @@ import android.annotation.UserIdInt;
import android.content.ClipData;
import android.content.Context;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
@@ -158,7 +159,9 @@ public abstract class WindowManagerInternal {
public interface WindowsForAccessibilityCallback {
/**
- * Called when the windows for accessibility changed.
+ * Called when the windows for accessibility changed. This is called if
+ * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y} is
+ * false.
*
* @param forceSend Send the windows for accessibility even if they haven't changed.
* @param topFocusedDisplayId The display Id which has the top focused window.
@@ -167,6 +170,23 @@ public abstract class WindowManagerInternal {
*/
void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId,
IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows);
+
+ /**
+ * Called when the windows for accessibility changed. This is called if
+ * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y} is
+ * true.
+ * TODO(b/322444245): Remove screenSize parameter by getting it from
+ * DisplayManager#getDisplay(int).getRealSize() on the a11y side.
+ *
+ * @param forceSend Send the windows for accessibility even if they haven't changed.
+ * @param topFocusedDisplayId The display Id which has the top focused window.
+ * @param topFocusedWindowToken The window token of top focused window.
+ * @param screenSize The size of the display that the change happened.
+ * @param windows The windows for accessibility.
+ */
+ void onAccessibilityWindowsChanged(boolean forceSend, int topFocusedDisplayId,
+ @NonNull IBinder topFocusedWindowToken, @NonNull Point screenSize,
+ @NonNull List<AccessibilityWindowsPopulator.AccessibilityWindow> windows);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8ebe82656cc5..9b7bc4383e0c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -306,11 +306,11 @@ import android.view.displayhash.VerifiedDisplayHash;
import android.view.inputmethod.ImeTracker;
import android.window.AddToSurfaceSyncGroupResult;
import android.window.ClientWindowFrames;
+import android.window.IGlobalDragListener;
import android.window.IScreenRecordingCallback;
import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
import android.window.ITrustedPresentationListener;
-import android.window.IUnhandledDragListener;
import android.window.InputTransferToken;
import android.window.ScreenCapture;
import android.window.SystemPerformanceHinter;
@@ -10020,14 +10020,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
/**
- * Sets the listener to be called back when a cross-window drag and drop operation is unhandled
- * (ie. not handled by any window which can handle the drag).
+ * Sets the listener to be called back when a cross-window drag and drop operation happens.
*/
@Override
- public void setUnhandledDragListener(IUnhandledDragListener listener) throws RemoteException {
+ public void setGlobalDragListener(IGlobalDragListener listener) throws RemoteException {
mAtmService.enforceTaskPermission("setUnhandledDragListener");
synchronized (mGlobalLock) {
- mDragDropController.setUnhandledDragListener(listener);
+ mDragDropController.setGlobalDragListener(listener);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index a8de9198943c..d6fc01aeadd2 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -2254,6 +2254,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
ownerTask.addChild(taskFragment, position);
taskFragment.setWindowingMode(creationParams.getWindowingMode());
if (!creationParams.getInitialRelativeBounds().isEmpty()) {
+ // The surface operations for the task fragment should sync with the transition.
+ // This avoid using pending transaction before collectExistenceChange is called.
+ if (transition != null) {
+ addToSyncSet(transition.getSyncId(), taskFragment);
+ }
// Set relative bounds instead of using setBounds. This will avoid unnecessary update in
// case the parent has resized since the last time parent info is sent to the organizer.
taskFragment.setRelativeEmbeddedBounds(creationParams.getInitialRelativeBounds());
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 934f8a73a19d..90f5b62b4a08 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1897,11 +1897,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return true;
}
- if (android.permission.flags.Flags.sensitiveNotificationAppProtection()) {
- if (mWmService.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(getOwningPackage(), getOwningUid())) {
- return true;
- }
+ // block screen capture to protect sensitive notifications or content on the screen.
+ if (mWmService.mSensitiveContentPackages.shouldBlockScreenCaptureForApp(
+ getOwningPackage(), getOwningUid(), getWindowToken())) {
+ return true;
}
return !DevicePolicyCache.getInstance().isScreenCaptureAllowed(mShowUserId);
@@ -2839,10 +2838,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// For child windows we want to use the pid for the parent window in case the the child
// window was added from another process.
final WindowState parentWindow = getParentWindow();
- final int pid = parentWindow != null ? parentWindow.mSession.mPid : mSession.mPid;
- final Configuration processConfig =
- mWmService.mAtmService.getGlobalConfigurationForPid(pid);
- return processConfig;
+ final Session session = parentWindow != null ? parentWindow.mSession : mSession;
+ return session.mPid == MY_PID ? mWmService.mRoot.getConfiguration()
+ : session.mProcess.getConfiguration();
}
private Configuration getLastReportedConfiguration() {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index d821625c1076..c778398342dc 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1509,15 +1509,14 @@ bool NativeInputManager::filterInputEvent(const InputEvent& inputEvent, uint32_t
ScopedLocalRef<jobject> inputEventObj(env);
switch (inputEvent.getType()) {
case InputEventType::KEY:
- inputEventObj.reset(
- android_view_KeyEvent_fromNative(env,
- static_cast<const KeyEvent&>(inputEvent)));
+ inputEventObj =
+ android_view_KeyEvent_obtainAsCopy(env,
+ static_cast<const KeyEvent&>(inputEvent));
break;
case InputEventType::MOTION:
- inputEventObj.reset(
- android_view_MotionEvent_obtainAsCopy(env,
- static_cast<const MotionEvent&>(
- inputEvent)));
+ inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
+ static_cast<const MotionEvent&>(
+ inputEvent));
break;
default:
return true; // dispatch the event normally
@@ -1559,7 +1558,7 @@ void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent& keyEvent,
const nsecs_t when = keyEvent.getEventTime();
JNIEnv* env = jniEnv();
- ScopedLocalRef<jobject> keyEventObj(env, android_view_KeyEvent_fromNative(env, keyEvent));
+ ScopedLocalRef<jobject> keyEventObj = android_view_KeyEvent_obtainAsCopy(env, keyEvent);
if (!keyEventObj.get()) {
ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing.");
return;
@@ -1639,7 +1638,7 @@ nsecs_t NativeInputManager::interceptKeyBeforeDispatching(const sp<IBinder>& tok
// Token may be null
ScopedLocalRef<jobject> tokenObj(env, javaObjectForIBinder(env, token));
- ScopedLocalRef<jobject> keyEventObj(env, android_view_KeyEvent_fromNative(env, keyEvent));
+ ScopedLocalRef<jobject> keyEventObj = android_view_KeyEvent_obtainAsCopy(env, keyEvent);
if (!keyEventObj.get()) {
ALOGE("Failed to obtain key event object for interceptKeyBeforeDispatching.");
return 0;
@@ -1670,7 +1669,7 @@ std::optional<KeyEvent> NativeInputManager::dispatchUnhandledKey(const sp<IBinde
// Note: tokenObj may be null.
ScopedLocalRef<jobject> tokenObj(env, javaObjectForIBinder(env, token));
- ScopedLocalRef<jobject> keyEventObj(env, android_view_KeyEvent_fromNative(env, keyEvent));
+ ScopedLocalRef<jobject> keyEventObj = android_view_KeyEvent_obtainAsCopy(env, keyEvent);
if (!keyEventObj.get()) {
ALOGE("Failed to obtain key event object for dispatchUnhandledKey.");
return {};
@@ -1689,7 +1688,8 @@ std::optional<KeyEvent> NativeInputManager::dispatchUnhandledKey(const sp<IBinde
return {};
}
- const KeyEvent fallbackEvent = android_view_KeyEvent_toNative(env, fallbackKeyEventObj.get());
+ const KeyEvent fallbackEvent =
+ android_view_KeyEvent_obtainAsCopy(env, fallbackKeyEventObj.get());
android_view_KeyEvent_recycle(env, fallbackKeyEventObj.get());
return fallbackEvent;
}
@@ -2070,7 +2070,7 @@ static jint nativeInjectInputEvent(JNIEnv* env, jobject nativeImplObj, jobject i
InputEventInjectionSync mode = static_cast<InputEventInjectionSync>(syncMode);
if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) {
- const KeyEvent keyEvent = android_view_KeyEvent_toNative(env, inputEventObj);
+ const KeyEvent keyEvent = android_view_KeyEvent_obtainAsCopy(env, inputEventObj);
const InputEventInjectionResult result =
im->getInputManager()->getDispatcher().injectInputEvent(&keyEvent, targetUid, mode,
std::chrono::milliseconds(
@@ -2101,7 +2101,7 @@ static jobject nativeVerifyInputEvent(JNIEnv* env, jobject nativeImplObj, jobjec
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) {
- const KeyEvent keyEvent = android_view_KeyEvent_toNative(env, inputEventObj);
+ const KeyEvent keyEvent = android_view_KeyEvent_obtainAsCopy(env, inputEventObj);
std::unique_ptr<VerifiedInputEvent> verifiedEvent =
im->getInputManager()->getDispatcher().verifyInputEvent(keyEvent);
if (verifiedEvent == nullptr) {
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index a46916553abc..b38a2f9558e9 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -117,6 +117,9 @@
<xs:element type="sensorDetails" name="proxSensor">
<xs:annotation name="final"/>
</xs:element>
+ <xs:element type="sensorDetails" name="tempSensor">
+ <xs:annotation name="final"/>
+ </xs:element>
<!-- Length of the ambient light horizon used to calculate the long & short term
estimates of ambient light in milliseconds.-->
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 79ea274e2fca..b329db4a2076 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -133,6 +133,7 @@ package com.android.server.display.config {
method public final java.math.BigDecimal getScreenBrightnessRampSlowIncreaseIdle();
method public final com.android.server.display.config.SensorDetails getScreenOffBrightnessSensor();
method public final com.android.server.display.config.IntegerArray getScreenOffBrightnessSensorValueToLux();
+ method public final com.android.server.display.config.SensorDetails getTempSensor();
method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling();
method public final com.android.server.display.config.UsiVersion getUsiVersion();
method public final void setAmbientBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
@@ -167,6 +168,7 @@ package com.android.server.display.config {
method public final void setScreenBrightnessRampSlowIncreaseIdle(java.math.BigDecimal);
method public final void setScreenOffBrightnessSensor(com.android.server.display.config.SensorDetails);
method public final void setScreenOffBrightnessSensorValueToLux(com.android.server.display.config.IntegerArray);
+ method public final void setTempSensor(com.android.server.display.config.SensorDetails);
method public final void setThermalThrottling(@NonNull com.android.server.display.config.ThermalThrottling);
method public final void setUsiVersion(com.android.server.display.config.UsiVersion);
}
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index be4b9e1fc7b1..173cb36a1a34 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -104,7 +104,6 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR
flattenedPrimaryProviders.add(cn.flattenToString());
}
- final boolean isShowAllOptionsRequested = false;
mPendingIntent = mCredentialManagerUi.createPendingIntent(
RequestInfo.newCreateRequestInfo(
mRequestId, mClientRequest,
@@ -112,8 +111,8 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR
PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS),
/*defaultProviderId=*/flattenedPrimaryProviders,
- isShowAllOptionsRequested),
- providerDataList, /*isRequestForAllOptions=*/ isShowAllOptionsRequested);
+ /*isShowAllOptionsRequested=*/ false),
+ providerDataList);
mClientCallback.onPendingIntent(mPendingIntent);
} catch (RemoteException e) {
mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 534c842f6b07..9d45cfb2fb7e 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -18,6 +18,7 @@ package com.android.server.credentials;
import static android.credentials.selection.Constants.EXTRA_FINAL_RESPONSE_RECEIVER;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -152,16 +153,34 @@ public class CredentialManagerUi {
/**
* Creates a {@link PendingIntent} to be used to invoke the credential manager selector UI,
- * by the calling app process.
+ * by the calling app process. The bottom-sheet navigates to the default page when the intent
+ * is invoked.
*
* @param requestInfo the information about the request
* @param providerDataList the list of provider data from remote providers
- * @param isRequestForAllOptions whether the bottom sheet should directly navigate to the
- * all options page
*/
public PendingIntent createPendingIntent(
- RequestInfo requestInfo, ArrayList<ProviderData> providerDataList,
- boolean isRequestForAllOptions) {
+ RequestInfo requestInfo, ArrayList<ProviderData> providerDataList) {
+ return createPendingIntent(requestInfo, providerDataList, /*forAutofill=*/ false);
+ }
+
+ /**
+ * Creates a {@link PendingIntent} to be used to invoke the credential manager selector UI,
+ * by the calling app process. This intent is invoked from the Autofill flow, when the user
+ * requests to bring up the 'All Options' page of the credential bottom-sheet. When the user
+ * clicks on the pinned entry, the intent will bring up the 'All Options' page of the
+ * bottom-sheet. The provider data list is processed by the credential autofill service for
+ * each autofill id and passed in as an auth extra.
+ *
+ * @param requestInfo the information about the request
+ */
+ public PendingIntent createPendingIntentForAutofill(RequestInfo requestInfo) {
+ return createPendingIntent(requestInfo, /*providerDataList=*/ null, /*forAutofill=*/ true);
+ }
+
+ private PendingIntent createPendingIntent(
+ RequestInfo requestInfo, @Nullable ArrayList<ProviderData> providerDataList,
+ boolean forAutofill) {
List<CredentialProviderInfo> allProviders =
CredentialProviderInfoFactory.getCredentialProviderServices(
mContext,
@@ -176,11 +195,17 @@ public class CredentialManagerUi {
.map(disabledProvider -> new DisabledProviderData(
disabledProvider.getComponentName().flattenToString())).toList();
- Intent intent = IntentFactory.createCredentialSelectorIntent(mContext, requestInfo,
- providerDataList,
- new ArrayList<>(disabledProviderDataList), mResultReceiver,
- isRequestForAllOptions)
- .setAction(UUID.randomUUID().toString());
+ Intent intent;
+ if (forAutofill) {
+ intent = IntentFactory.createCredentialSelectorIntentForAutofill(
+ mContext, requestInfo, new ArrayList<>(disabledProviderDataList),
+ mResultReceiver);
+ } else {
+ intent = IntentFactory.createCredentialSelectorIntent(
+ mContext, requestInfo, providerDataList,
+ new ArrayList<>(disabledProviderDataList), mResultReceiver);
+ }
+ intent.setAction(UUID.randomUUID().toString());
//TODO: Create unique pending intent using request code and cancel any pre-existing pending
// intents
return PendingIntent.getActivityAsUser(
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index d06d4d869192..723c52f37574 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -115,15 +115,12 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ
}
cancelExistingPendingIntent();
- final boolean isShowAllOptionsRequested = true;
- mPendingIntent = mCredentialManagerUi.createPendingIntent(
+ mPendingIntent = mCredentialManagerUi.createPendingIntentForAutofill(
RequestInfo.newGetRequestInfo(
mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS),
- isShowAllOptionsRequested),
- /*providerDataList=*/ null,
- /*isRequestForAllOptions=*/ isShowAllOptionsRequested);
+ /*isShowAllOptionsRequested=*/ true));
List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
for (ProviderData providerData : providerDataList) {
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index a279337698f2..6513ae1af369 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -103,7 +103,6 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest,
Binder.withCleanCallingIdentity(() -> {
try {
cancelExistingPendingIntent();
- final boolean isShowAllOptionsRequested = false;
mPendingIntent = mCredentialManagerUi.createPendingIntent(
RequestInfo.newGetRequestInfo(
mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
@@ -111,9 +110,8 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest,
mClientAppInfo.getPackageName(),
Manifest.permission
.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS),
- isShowAllOptionsRequested),
- providerDataList,
- /*isRequestForAllOptions=*/ isShowAllOptionsRequested);
+ /*isShowAllOptionsRequested=*/ false),
+ providerDataList);
mClientCallback.onPendingIntent(mPendingIntent);
} catch (RemoteException e) {
mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index 23aa3742175a..96ef2ed61f1a 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -531,7 +531,7 @@ public class MetricUtilities {
int index = 0;
for (CandidateBrowsingPhaseMetric metric : browsingPhaseMetrics) {
browsedClickedEntries[index] = metric.getEntryEnum();
- browsedProviderUid[index] = metric.getProviderUid();
+ browsedProviderUid[index] = DEFAULT_INT_32;
index++;
}
FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_FINALNOUID_REPORTED,
diff --git a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
index 6b313fda1dd2..6e8f7c8d7722 100644
--- a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
@@ -187,14 +187,13 @@ public class PrepareGetRequestSession extends GetRequestSession {
}
}
if (!providerDataList.isEmpty()) {
- final boolean isShowAllOptionsRequested = false;
return mCredentialManagerUi.createPendingIntent(
RequestInfo.newGetRequestInfo(
mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS),
- isShowAllOptionsRequested),
- providerDataList, /*isRequestForAllOptions=*/ isShowAllOptionsRequested);
+ /*isShowAllOptionsRequested=*/ false),
+ providerDataList);
} else {
return null;
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index ca23d62601bb..fa63bc899cb5 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -30,9 +30,7 @@ import android.credentials.selection.AuthenticationEntry;
import android.credentials.selection.Entry;
import android.credentials.selection.GetCredentialProviderData;
import android.credentials.selection.ProviderPendingIntentResponse;
-import android.os.Bundle;
import android.os.ICancellationSignal;
-import android.service.autofill.Flags;
import android.service.credentials.Action;
import android.service.credentials.BeginGetCredentialOption;
import android.service.credentials.BeginGetCredentialRequest;
@@ -44,7 +42,6 @@ import android.service.credentials.GetCredentialRequest;
import android.service.credentials.RemoteEntry;
import android.util.Pair;
import android.util.Slog;
-import android.view.autofill.AutofillId;
import java.util.ArrayList;
import java.util.HashMap;
@@ -77,10 +74,6 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
@NonNull
private final Map<String, CredentialOption> mBeginGetOptionToCredentialOptionMap;
- @NonNull
- private final Map<String, AutofillId> mCredentialEntryKeyToAutofilLIdMap;
-
-
/** The complete request to be used in the second round. */
private final android.credentials.GetCredentialRequest mCompleteRequest;
private final CallingAppInfo mCallingAppInfo;
@@ -249,7 +242,6 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
mBeginGetOptionToCredentialOptionMap = new HashMap<>(beginGetOptionToCredentialOptionMap);
mProviderResponseDataHandler = new ProviderResponseDataHandler(
ComponentName.unflattenFromString(hybridService));
- mCredentialEntryKeyToAutofilLIdMap = new HashMap<>();
}
/** Called when the provider response has been updated by an external source. */
@@ -303,7 +295,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
invokeCallbackOnInternalInvalidState();
return;
}
- onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
+ onCredentialEntrySelected(providerPendingIntentResponse);
break;
case ACTION_ENTRY_KEY:
Action actionEntry = mProviderResponseDataHandler.getActionEntry(entryKey);
@@ -312,7 +304,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
invokeCallbackOnInternalInvalidState();
return;
}
- onActionEntrySelected(providerPendingIntentResponse, entryKey);
+ onActionEntrySelected(providerPendingIntentResponse);
break;
case AUTHENTICATION_ACTION_ENTRY_KEY:
Action authenticationEntry = mProviderResponseDataHandler
@@ -342,7 +334,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
break;
case REMOTE_ENTRY_KEY:
if (mProviderResponseDataHandler.getRemoteEntry(entryKey) != null) {
- onRemoteEntrySelected(providerPendingIntentResponse, entryKey);
+ onRemoteEntrySelected(providerPendingIntentResponse);
} else {
Slog.i(TAG, "Unexpected remote entry key");
invokeCallbackOnInternalInvalidState();
@@ -381,7 +373,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
return null;
}
- private Intent setUpFillInIntentWithFinalRequest(@NonNull String id, String entryKey) {
+ private Intent setUpFillInIntentWithFinalRequest(@NonNull String id) {
// TODO: Determine if we should skip this entry if entry id is not set, or is set
// but does not resolve to a valid option. For now, not skipping it because
// it may be possible that the provider adds their own extras and expects to receive
@@ -392,13 +384,6 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
Slog.w(TAG, "Id from Credential Entry does not resolve to a valid option");
return intent;
}
- AutofillId autofillId = credentialOption
- .getCandidateQueryData()
- .getParcelable(CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class);
- if (autofillId != null && Flags.autofillCredmanIntegration()) {
- intent.putExtra(CredentialProviderService.EXTRA_AUTOFILL_ID, autofillId);
- mCredentialEntryKeyToAutofilLIdMap.put(entryKey, autofillId);
- }
return intent.putExtra(
CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
new GetCredentialRequest(
@@ -414,13 +399,12 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
}
private void onRemoteEntrySelected(
- ProviderPendingIntentResponse providerPendingIntentResponse, String entryKey) {
- onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
+ ProviderPendingIntentResponse providerPendingIntentResponse) {
+ onCredentialEntrySelected(providerPendingIntentResponse);
}
private void onCredentialEntrySelected(
- ProviderPendingIntentResponse providerPendingIntentResponse,
- String entryKey) {
+ ProviderPendingIntentResponse providerPendingIntentResponse) {
if (providerPendingIntentResponse == null) {
invokeCallbackOnInternalInvalidState();
return;
@@ -437,18 +421,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
GetCredentialResponse getCredentialResponse = PendingIntentResultHandler
.extractGetCredentialResponse(
providerPendingIntentResponse.getResultData());
- if (getCredentialResponse != null && getCredentialResponse.getCredential() != null) {
- Bundle credentialData = getCredentialResponse.getCredential().getData();
- AutofillId autofillId = mCredentialEntryKeyToAutofilLIdMap.get(entryKey);
- if (Flags.autofillCredmanIntegration()
- && entryKey != null && autofillId != null && credentialData != null
- ) {
- Slog.d(TAG, "Adding autofillId to credential response: " + autofillId);
- credentialData.putParcelable(
- CredentialProviderService.EXTRA_AUTOFILL_ID,
- mCredentialEntryKeyToAutofilLIdMap.get(entryKey)
- );
- }
+ if (getCredentialResponse != null) {
mCallbacks.onFinalResponseReceived(mComponentName,
getCredentialResponse);
return;
@@ -532,9 +505,9 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
/** Returns true if either an exception or a response is found. */
private void onActionEntrySelected(ProviderPendingIntentResponse
- providerPendingIntentResponse, String entryKey) {
+ providerPendingIntentResponse) {
Slog.i(TAG, "onActionEntrySelected");
- onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
+ onCredentialEntrySelected(providerPendingIntentResponse);
}
@@ -632,7 +605,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
Entry entry = new Entry(CREDENTIAL_ENTRY_KEY,
id, credentialEntry.getSlice(),
setUpFillInIntentWithFinalRequest(credentialEntry
- .getBeginGetCredentialOptionId(), id));
+ .getBeginGetCredentialOptionId()));
mUiCredentialEntries.put(id, new Pair<>(credentialEntry, entry));
mCredentialEntryTypes.add(credentialEntry.getType());
}
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index a212812b0768..c16c61271280 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -1012,7 +1012,11 @@ public class MidiService extends IMidiManager.Stub {
}
}
- if (user.getUserIdentifier() == mUserManager.getMainUser().getIdentifier()) {
+ // Allow only the main user to create BluetoothMidiService.
+ // If there is no main user, allow all users to create it.
+ UserHandle mainUser = mUserManager.getMainUser();
+ if ((mainUser == null)
+ || (user.getUserIdentifier() == mainUser.getIdentifier())) {
PackageInfo info;
try {
info = mPackageManager.getPackageInfoAsUser(
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index afd6dbd7f6a7..b7af58c0fd54 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -85,7 +85,6 @@ android_ravenwood_test {
srcs: [
"src/com/android/server/inputmethod/**/ClientControllerTest.java",
],
- sdk_version: "test_current",
auto_gen_config: true,
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java
index dc9631a8f2e2..9e3d9ec6b9b6 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java
@@ -32,10 +32,8 @@ import android.content.pm.PackageManagerInternal;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import android.view.Display;
-import android.view.inputmethod.InputBinding;
import com.android.internal.inputmethod.IInputMethodClient;
import com.android.internal.inputmethod.IRemoteInputConnection;
@@ -53,7 +51,7 @@ import java.util.concurrent.TimeUnit;
public final class ClientControllerTest {
private static final int ANY_DISPLAY_ID = Display.DEFAULT_DISPLAY;
private static final int ANY_CALLER_UID = 1;
- private static final int ANY_CALLER_PID = 1;
+ private static final int ANY_CALLER_PID = 2;
private static final String SOME_PACKAGE_NAME = "some.package";
@Rule
@@ -82,13 +80,16 @@ public final class ClientControllerTest {
mController = new ClientController(mMockPackageManagerInternal);
}
+ // TODO(b/322895594): No need to directly invoke create$ravenwood once b/322895594 is fixed.
+ private IInputMethodClientInvoker createInvoker(IInputMethodClient client, Handler handler) {
+ return RavenwoodRule.isOnRavenwood()
+ ? IInputMethodClientInvoker.create$ravenwood(client, handler) :
+ IInputMethodClientInvoker.create(client, handler);
+ }
+
@Test
- // TODO(b/314150112): Enable host side mode for this test once Ravenwood is enabled for
- // inputmethod server classes.
- @IgnoreUnderRavenwood(blockedBy = {InputBinding.class, IInputMethodClientInvoker.class})
public void testAddClient_cannotAddTheSameClientTwice() {
- var invoker = IInputMethodClientInvoker.create(mClient, mHandler);
-
+ final var invoker = createInvoker(mClient, mHandler);
synchronized (ImfLock.class) {
mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID,
ANY_CALLER_PID);
@@ -101,18 +102,17 @@ public final class ClientControllerTest {
}
});
assertThat(thrown.getMessage()).isEqualTo(
- "uid=1/pid=1/displayId=0 is already registered");
+ "uid=" + ANY_CALLER_UID + "/pid=" + ANY_CALLER_PID
+ + "/displayId=0 is already registered");
}
}
@Test
- // TODO(b/314150112): Enable host side mode for this test once Ravenwood is enabled for
- // inputmethod server classes.
- @IgnoreUnderRavenwood(blockedBy = {InputBinding.class, IInputMethodClientInvoker.class})
public void testAddClient() throws Exception {
+ final var invoker = createInvoker(mClient, mHandler);
synchronized (ImfLock.class) {
- var invoker = IInputMethodClientInvoker.create(mClient, mHandler);
- var added = mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID,
+ final var added = mController.addClient(invoker, mConnection, ANY_DISPLAY_ID,
+ ANY_CALLER_UID,
ANY_CALLER_PID);
verify(invoker.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
@@ -121,16 +121,12 @@ public final class ClientControllerTest {
}
@Test
- // TODO(b/314150112): Enable host side mode for this test once Ravenwood is enabled for
- // inputmethod server classes.
- @IgnoreUnderRavenwood(blockedBy = {InputBinding.class, IInputMethodClientInvoker.class})
public void testRemoveClient() {
- var callback = new TestClientControllerCallback();
+ final var invoker = createInvoker(mClient, mHandler);
+ final var callback = new TestClientControllerCallback();
ClientState added;
synchronized (ImfLock.class) {
mController.addClientControllerCallback(callback);
-
- var invoker = IInputMethodClientInvoker.create(mClient, mHandler);
added = mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID,
ANY_CALLER_PID);
assertThat(mController.getClient(invoker.asBinder())).isSameInstanceAs(added);
@@ -138,21 +134,17 @@ public final class ClientControllerTest {
}
// Test callback
- var removed = callback.waitForRemovedClient(5, TimeUnit.SECONDS);
+ final var removed = callback.waitForRemovedClient(5, TimeUnit.SECONDS);
assertThat(removed).isSameInstanceAs(added);
}
@Test
- // TODO(b/314150112): Enable host side mode for this test once Ravenwood is enabled for
- // inputmethod server classes and updated to newer Mockito with static mock support (mock
- // InputMethodUtils#checkIfPackageBelongsToUid instead of PackageManagerInternal#isSameApp)
- @IgnoreUnderRavenwood(blockedBy = {InputMethodUtils.class})
public void testVerifyClientAndPackageMatch() {
+ final var invoker = createInvoker(mClient, mHandler);
when(mMockPackageManagerInternal.isSameApp(eq(SOME_PACKAGE_NAME), /* flags= */
anyLong(), eq(ANY_CALLER_UID), /* userId= */ anyInt())).thenReturn(true);
synchronized (ImfLock.class) {
- var invoker = IInputMethodClientInvoker.create(mClient, mHandler);
mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID,
ANY_CALLER_PID);
assertThat(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
index 8faaf5998d13..05c243fda2b8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -43,6 +43,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.server.display.BrightnessThrottler.Injector;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
import com.android.server.display.mode.DisplayModeDirectorTest;
import org.junit.Before;
@@ -56,6 +57,7 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -292,6 +294,53 @@ public class BrightnessThrottlerTest {
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
}
+
+ @Test
+ public void testThermalThrottlingWithDisplaySensor() throws Exception {
+ final ThrottlingLevel level =
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f);
+ List<ThrottlingLevel> levels = new ArrayList<>(List.of(level));
+ final ThermalBrightnessThrottlingData data = ThermalBrightnessThrottlingData.create(levels);
+ final SensorData tempSensor = new SensorData("DISPLAY", "VIRTUAL-SKIN-DISPLAY");
+ final BrightnessThrottler throttler =
+ createThrottlerSupportedWithTempSensor(data, tempSensor);
+ assertTrue(throttler.deviceSupportsThrottling());
+
+ verify(mThermalServiceMock)
+ .registerThermalEventListenerWithType(
+ mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_DISPLAY));
+ final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+
+ // Set VIRTUAL-SKIN-DISPLAY tatus too low to verify no throttling.
+ listener.notifyThrottling(getDisplayTempWithName(tempSensor.name, level.thermalStatus - 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+ // Verify when skin sensor throttled, no brightness throttling triggered.
+ listener.notifyThrottling(getSkinTemp(level.thermalStatus + 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+ // Verify when display sensor of another name throttled, no brightness throttling triggered.
+ listener.notifyThrottling(getDisplayTempWithName("ANOTHER-NAME", level.thermalStatus + 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+ // Verify when display sensor of current name throttled, brightness throttling triggered.
+ listener.notifyThrottling(getDisplayTempWithName(tempSensor.name, level.thermalStatus + 1));
+ mTestLooper.dispatchAll();
+ assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+ throttler.getBrightnessMaxReason());
+ }
+
@Test public void testUpdateThermalThrottlingData() throws Exception {
// Initialise brightness throttling levels
// Ensure that they are overridden by setting the data through device config.
@@ -476,18 +525,30 @@ public class BrightnessThrottlerTest {
return new BrightnessThrottler(mInjectorMock, mHandler, mHandler,
/* throttlingChangeCallback= */ () -> {}, /* uniqueDisplayId= */ null,
/* thermalThrottlingDataId= */ null,
- /* thermalThrottlingDataMap= */ new HashMap<>(1));
+ /* thermalThrottlingDataMap= */ new HashMap<>(1),
+ /* tempSensor= */ null);
}
private BrightnessThrottler createThrottlerSupported(ThermalBrightnessThrottlingData data) {
+ SensorData tempSensor = SensorData.loadTempSensorUnspecifiedConfig();
+ return createThrottlerSupportedWithTempSensor(data, tempSensor);
+ }
+ private BrightnessThrottler createThrottlerSupportedWithTempSensor(
+ ThermalBrightnessThrottlingData data, SensorData tempSensor) {
assertNotNull(data);
- HashMap<String, ThermalBrightnessThrottlingData> throttlingDataMap = new HashMap<>(1);
+ Map<String, ThermalBrightnessThrottlingData> throttlingDataMap = new HashMap<>(1);
throttlingDataMap.put("default", data);
return new BrightnessThrottler(mInjectorMock, mHandler, BackgroundThread.getHandler(),
- () -> {}, "123", "default", throttlingDataMap);
+ () -> {}, "123", "default", throttlingDataMap, tempSensor);
}
private Temperature getSkinTemp(@ThrottlingStatus int status) {
return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
}
+
+ private Temperature getDisplayTempWithName(
+ String sensorName, @ThrottlingStatus int status) {
+ assertNotNull(sensorName);
+ return new Temperature(30.0f, Temperature.TYPE_DISPLAY, sensorName, status);
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index b29fc8828f58..2867041511b5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -20,6 +20,7 @@ package com.android.server.display;
import static com.android.internal.display.BrightnessSynchronizer.brightnessIntToFloat;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
+import static com.android.server.display.config.SensorData.TEMPERATURE_TYPE_SKIN;
import static com.android.server.display.config.SensorData.SupportedMode;
import static com.android.server.display.utils.DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat;
import static com.android.server.display.utils.DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat;
@@ -106,6 +107,7 @@ public final class DisplayDeviceConfigTest {
MockitoAnnotations.initMocks(this);
when(mContext.getResources()).thenReturn(mResources);
when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(true);
+ when(mFlags.isSensorBasedBrightnessThrottlingEnabled()).thenReturn(true);
mockDeviceConfigs();
}
@@ -143,6 +145,8 @@ public final class DisplayDeviceConfigTest {
assertEquals("", mDisplayDeviceConfig.getAmbientLightSensor().name);
assertNull(mDisplayDeviceConfig.getProximitySensor().type);
assertNull(mDisplayDeviceConfig.getProximitySensor().name);
+ assertEquals(TEMPERATURE_TYPE_SKIN, mDisplayDeviceConfig.getTempSensor().type);
+ assertNull(mDisplayDeviceConfig.getTempSensor().name);
assertTrue(mDisplayDeviceConfig.isAutoBrightnessAvailable());
}
@@ -592,6 +596,13 @@ public final class DisplayDeviceConfigTest {
}
@Test
+ public void testTempSensorFromDisplayConfig() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile();
+ assertEquals("DISPLAY", mDisplayDeviceConfig.getTempSensor().type);
+ assertEquals("VIRTUAL-SKIN-DISPLAY", mDisplayDeviceConfig.getTempSensor().name);
+ }
+
+ @Test
public void testBlockingZoneThresholdsFromDisplayConfig() throws IOException {
setupDisplayDeviceConfigFromDisplayConfigFile();
@@ -1356,6 +1367,10 @@ public final class DisplayDeviceConfigTest {
+ "<name>Test Binned Brightness Sensor</name>\n"
+ "</screenOffBrightnessSensor>\n"
+ proxSensor
+ + "<tempSensor>\n"
+ + "<type>DISPLAY</type>\n"
+ + "<name>VIRTUAL-SKIN-DISPLAY</name>\n"
+ + "</tempSensor>\n"
+ "<ambientBrightnessChangeThresholds>\n"
+ "<brighteningThresholds>\n"
+ "<minimum>10</minimum>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
index 37d0f6250aaf..34f352e7bf54 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
@@ -37,10 +37,14 @@ import com.android.internal.annotations.Keep;
import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.testutils.FakeDeviceConfigInterface;
import com.android.server.testutils.TestHandler;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,9 +54,6 @@ import org.mockito.MockitoAnnotations;
import java.util.List;
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
@RunWith(JUnitParamsRunner.class)
public class BrightnessThermalClamperTest {
@@ -125,13 +126,13 @@ public class BrightnessThermalClamperTest {
public void testNotifyThrottlingAfterOnDisplayChange(List<ThrottlingLevel> throttlingLevels,
@Temperature.ThrottlingStatus int throttlingStatus,
boolean expectedActive, float expectedBrightness) throws RemoteException {
- IThermalEventListener thermalEventListener = captureThermalEventListener();
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
mClamper.onDisplayChanged(new TestThermalData(throttlingLevels));
mTestHandler.flush();
assertFalse(mClamper.isActive());
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- thermalEventListener.notifyThrottling(createTemperature(throttlingStatus));
+ thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
mTestHandler.flush();
assertEquals(expectedActive, mClamper.isActive());
assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -139,11 +140,11 @@ public class BrightnessThermalClamperTest {
@Test
@Parameters(method = "testThrottlingData")
- public void testOnDisplayChangeAfterNotifyThrottlng(List<ThrottlingLevel> throttlingLevels,
+ public void testOnDisplayChangeAfterNotifyThrottling(List<ThrottlingLevel> throttlingLevels,
@Temperature.ThrottlingStatus int throttlingStatus,
boolean expectedActive, float expectedBrightness) throws RemoteException {
- IThermalEventListener thermalEventListener = captureThermalEventListener();
- thermalEventListener.notifyThrottling(createTemperature(throttlingStatus));
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
mTestHandler.flush();
assertFalse(mClamper.isActive());
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -156,8 +157,8 @@ public class BrightnessThermalClamperTest {
@Test
public void testOverrideData() throws RemoteException {
- IThermalEventListener thermalEventListener = captureThermalEventListener();
- thermalEventListener.notifyThrottling(createTemperature(Temperature.THROTTLING_SEVERE));
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ thermalEventListener.notifyThrottling(createSkinTemperature(Temperature.THROTTLING_SEVERE));
mTestHandler.flush();
assertFalse(mClamper.isActive());
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -183,15 +184,60 @@ public class BrightnessThermalClamperTest {
assertEquals(0.4f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
}
- private IThermalEventListener captureThermalEventListener() throws RemoteException {
+ @Test
+ public void testDisplaySensorBasedThrottling() throws RemoteException {
+ final int severity = PowerManager.THERMAL_STATUS_SEVERE;
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ // Update config to listen to display type sensor.
+ final SensorData tempSensor = new SensorData("DISPLAY", "VIRTUAL-SKIN-DISPLAY");
+ final TestThermalData thermalData =
+ new TestThermalData(
+ DISPLAY_ID,
+ DisplayDeviceConfig.DEFAULT_ID,
+ List.of(new ThrottlingLevel(severity, 0.5f)),
+ tempSensor);
+ mClamper.onDisplayChanged(thermalData);
+ mTestHandler.flush();
+ verify(mMockThermalService).unregisterThermalEventListener(thermalEventListener);
+ thermalEventListener = captureThermalEventListener(Temperature.TYPE_DISPLAY);
+ assertFalse(mClamper.isActive());
+
+ // Verify no throttling triggered when any other sensor notification received.
+ thermalEventListener.notifyThrottling(createSkinTemperature(severity));
+ mTestHandler.flush();
+ assertFalse(mClamper.isActive());
+
+ thermalEventListener.notifyThrottling(createDisplayTemperature("OTHER-SENSOR", severity));
+ mTestHandler.flush();
+ assertFalse(mClamper.isActive());
+
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+
+ // Verify throttling triggered when display sensor of given name throttled.
+ thermalEventListener.notifyThrottling(createDisplayTemperature(tempSensor.name, severity));
+ mTestHandler.flush();
+ assertTrue(mClamper.isActive());
+ assertEquals(0.5f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+ }
+
+ private IThermalEventListener captureSkinThermalEventListener() throws RemoteException {
+ return captureThermalEventListener(Temperature.TYPE_SKIN);
+ }
+
+ private IThermalEventListener captureThermalEventListener(int type) throws RemoteException {
ArgumentCaptor<IThermalEventListener> captor = ArgumentCaptor.forClass(
IThermalEventListener.class);
verify(mMockThermalService).registerThermalEventListenerWithType(captor.capture(), eq(
- Temperature.TYPE_SKIN));
+ type));
return captor.getValue();
}
- private Temperature createTemperature(@Temperature.ThrottlingStatus int status) {
+ private Temperature createDisplayTemperature(
+ @NonNull String sensorName, @Temperature.ThrottlingStatus int status) {
+ return new Temperature(100, Temperature.TYPE_DISPLAY, sensorName, status);
+ }
+
+ private Temperature createSkinTemperature(@Temperature.ThrottlingStatus int status) {
return new Temperature(100, Temperature.TYPE_SKIN, "test_temperature", status);
}
@@ -217,19 +263,26 @@ public class BrightnessThermalClamperTest {
private final String mUniqueDisplayId;
private final String mDataId;
private final ThermalBrightnessThrottlingData mData;
+ private final SensorData mTempSensor;
private TestThermalData() {
- this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, null);
+ this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, null,
+ SensorData.loadTempSensorUnspecifiedConfig());
}
private TestThermalData(List<ThrottlingLevel> data) {
- this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, data);
+ this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, data,
+ SensorData.loadTempSensorUnspecifiedConfig());
}
- private TestThermalData(String uniqueDisplayId, String dataId, List<ThrottlingLevel> data) {
+
+ private TestThermalData(String uniqueDisplayId, String dataId, List<ThrottlingLevel> data,
+ SensorData tempSensor) {
mUniqueDisplayId = uniqueDisplayId;
mDataId = dataId;
mData = ThermalBrightnessThrottlingData.create(data);
+ mTempSensor = tempSensor;
}
+
@NonNull
@Override
public String getUniqueDisplayId() {
@@ -247,5 +300,11 @@ public class BrightnessThermalClamperTest {
public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() {
return mData;
}
+
+ @NonNull
+ @Override
+ public SensorData getTempSensor() {
+ return mTempSensor;
+ }
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java
new file mode 100644
index 000000000000..dad36e787360
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.media.projection.MediaProjectionInfo;
+import android.media.projection.MediaProjectionManager;
+import android.os.Binder;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.SensitiveContentPackages.PackageInfo;
+import com.android.server.wm.WindowManagerInternal;
+
+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.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RequiresFlagsEnabled(FLAG_SENSITIVE_CONTENT_APP_PROTECTION)
+/**
+ * Test {@link SensitiveContentProtectionManagerService} for sensitive on screen content
+ * protection, the service protects sensitive content during screen share.
+ */
+public class SensitiveContentProtectionManagerServiceContentTest {
+ private final PackageInfo mPackageInfo =
+ new PackageInfo("test.package", 12345, new Binder());
+ private SensitiveContentProtectionManagerService mSensitiveContentProtectionManagerService;
+ private MediaProjectionManager.Callback mMediaPorjectionCallback;
+
+ @Mock private WindowManagerInternal mWindowManager;
+ @Mock private MediaProjectionManager mProjectionManager;
+ @Mock private MediaProjectionInfo mMediaProjectionInfo;
+
+ @Captor
+ private ArgumentCaptor<MediaProjectionManager.Callback> mMediaProjectionCallbackCaptor;
+ @Captor
+ private ArgumentCaptor<ArraySet<PackageInfo>> mPackageInfoCaptor;
+
+ @Rule
+ public final TestableContext mContext =
+ new TestableContext(getInstrumentation().getTargetContext(), null);
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mSensitiveContentProtectionManagerService =
+ new SensitiveContentProtectionManagerService(mContext);
+ mSensitiveContentProtectionManagerService.init(mProjectionManager, mWindowManager);
+ verify(mProjectionManager).addCallback(mMediaProjectionCallbackCaptor.capture(), any());
+ mMediaPorjectionCallback = mMediaProjectionCallbackCaptor.getValue();
+ }
+
+ @Test
+ public void testBlockAppWindowForScreenCapture() {
+ mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
+ verify(mWindowManager, atLeast(1))
+ .addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
+ assertThat(mPackageInfoCaptor.getValue()).containsExactly(mPackageInfo);
+ }
+
+ @Test
+ public void testUnblockAppWindowForScreenCapture() {
+ mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), false);
+ verify(mWindowManager).removeBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
+ assertThat(mPackageInfoCaptor.getValue()).containsExactly(mPackageInfo);
+ }
+
+ @Test
+ public void testAppWindowIsUnblockedBeforeScreenCapture() {
+ // when screen sharing is not active, no app window should be blocked.
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ @Test
+ public void testAppWindowsAreUnblockedOnScreenCaptureEnd() {
+ mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
+ // when screen sharing ends, all blocked app windows should be cleared.
+ mMediaPorjectionCallback.onStop(mMediaProjectionInfo);
+ verify(mWindowManager).clearBlockedApps();
+ }
+
+ @Test
+ public void testDeveloperOptionDisableFeature() {
+ mockDisabledViaDeveloperOption();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mMediaProjectionInfo);
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ private void mockDisabledViaDeveloperOption() {
+ Settings.Global.putInt(
+ mContext.getContentResolver(),
+ Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
+ 1);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
index c298d516c89e..08050a9b30e0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
@@ -16,10 +16,10 @@
package com.android.server;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doCallRealMethod;
@@ -67,7 +67,11 @@ import java.util.Set;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
-public class SensitiveContentProtectionManagerServiceTest {
+/**
+ * Test {@link SensitiveContentProtectionManagerService} for sensitive notification protection,
+ * the service protects sensitive content during screen share.
+ */
+public class SensitiveContentProtectionManagerServiceNotificationTest {
private static final String NOTIFICATION_KEY_1 = "com.android.server.notification.TEST_KEY_1";
private static final String NOTIFICATION_KEY_2 = "com.android.server.notification.TEST_KEY_2";
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index a4761555384e..99752212fcbd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.alarm;
import static android.Manifest.permission.SCHEDULE_EXACT_ALARM;
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_UNFROZEN;
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED;
@@ -42,6 +44,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.NULL_DEFAULT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
@@ -135,6 +138,7 @@ import android.net.Uri;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -145,9 +149,11 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.platform.test.flag.util.FlagSetException;
import android.provider.DeviceConfig;
-import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.ArraySet;
import android.util.Log;
@@ -215,6 +221,7 @@ public final class AlarmManagerServiceTest {
private AppStateTrackerImpl.Listener mListener;
private AlarmManagerService.UninstallReceiver mPackageChangesReceiver;
private AlarmManagerService.ChargingReceiver mChargingReceiver;
+ private ActivityManager.UidFrozenStateChangedCallback mUidFrozenStateCallback;
private IAppOpsCallback mIAppOpsCallback;
private IAlarmManager mBinder;
@Mock
@@ -240,6 +247,8 @@ public final class AlarmManagerServiceTest {
@Mock
private ActivityManagerInternal mActivityManagerInternal;
@Mock
+ private ActivityManager mActivityManager;
+ @Mock
private PackageManagerInternal mPackageManagerInternal;
@Mock
private AppStateTrackerImpl mAppStateTracker;
@@ -403,15 +412,31 @@ public final class AlarmManagerServiceTest {
.mockStatic(PermissionChecker.class)
.mockStatic(PermissionManagerService.class)
.mockStatic(ServiceManager.class)
- .mockStatic(Settings.Global.class)
.mockStatic(SystemProperties.class)
.spyStatic(UserHandle.class)
.afterSessionFinished(
() -> LocalServices.removeServiceForTest(AlarmManagerInternal.class))
.build();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(NULL_DEFAULT);
+
+ /**
+ * Have to do this to switch the {@link Flags} implementation to {@link FakeFeatureFlagsImpl}.
+ * All methods that need any flag enabled should use the
+ * {@link android.platform.test.annotations.EnableFlags} annotation, in which case disabling
+ * the flag will fail with an exception that we will swallow here.
+ */
+ private void disableFlagsNotSetByAnnotation() {
+ try {
+ mSetFlagsRule.disableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS);
+ } catch (FlagSetException fse) {
+ // Expected if the test about to be run requires this enabled.
+ }
+ }
+
@Before
- public final void setUp() {
+ public void setUp() {
doReturn(mIActivityManager).when(ActivityManager::getService);
doReturn(mDeviceIdleInternal).when(
() -> LocalServices.getService(DeviceIdleInternal.class));
@@ -469,6 +494,7 @@ public final class AlarmManagerServiceTest {
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
when(mMockContext.getSystemService(BatteryManager.class)).thenReturn(mBatteryManager);
+ when(mMockContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager);
registerAppIds(new String[]{TEST_CALLING_PACKAGE},
new Integer[]{UserHandle.getAppId(TEST_CALLING_UID)});
@@ -479,7 +505,18 @@ public final class AlarmManagerServiceTest {
mService = new AlarmManagerService(mMockContext, mInjector);
spyOn(mService);
+ disableFlagsNotSetByAnnotation();
+
mService.onStart();
+
+ if (Flags.useFrozenStateToDropListenerAlarms()) {
+ final ArgumentCaptor<ActivityManager.UidFrozenStateChangedCallback> frozenCaptor =
+ ArgumentCaptor.forClass(ActivityManager.UidFrozenStateChangedCallback.class);
+ verify(mActivityManager).registerUidFrozenStateChangedCallback(
+ any(HandlerExecutor.class), frozenCaptor.capture());
+ mUidFrozenStateCallback = frozenCaptor.getValue();
+ }
+
// Unable to mock mMockContext to return a mock stats manager.
// So just mocking the whole MetricsHelper instance.
mService.mMetricsHelper = mock(MetricsHelper.class);
@@ -3741,9 +3778,87 @@ public final class AlarmManagerServiceTest {
mListener.handleUidCachedChanged(TEST_CALLING_UID, true);
assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED);
assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
mListener.handleUidCachedChanged(TEST_CALLING_UID_2, true);
assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED);
+ assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ assertEquals(2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
+ }
+
+ private void executeUidFrozenStateCallback(int[] uids, int[] frozenStates) {
+ assertNotNull(mUidFrozenStateCallback);
+ mUidFrozenStateCallback.onUidFrozenStateChanged(uids, frozenStates);
+ }
+
+ @EnableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS)
+ @Test
+ public void exactListenerAlarmsRemovedOnFrozen() {
+ mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true);
+
+ setTestAlarmWithListener(ELAPSED_REALTIME, 31, getNewListener(() -> {}), WINDOW_EXACT,
+ TEST_CALLING_UID);
+ setTestAlarmWithListener(RTC, 42, getNewListener(() -> {}), 56, TEST_CALLING_UID);
+ setTestAlarm(ELAPSED_REALTIME, 54, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
+ TEST_CALLING_UID, null);
+ setTestAlarm(RTC, 49, 154, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null);
+
+ setTestAlarmWithListener(ELAPSED_REALTIME, 21, getNewListener(() -> {}), WINDOW_EXACT,
+ TEST_CALLING_UID_2);
+ setTestAlarmWithListener(RTC, 412, getNewListener(() -> {}), 561, TEST_CALLING_UID_2);
+ setTestAlarm(ELAPSED_REALTIME, 26, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
+ TEST_CALLING_UID_2, null);
+ setTestAlarm(RTC, 549, 234, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID_2, null);
+
+ assertEquals(8, mService.mAlarmStore.size());
+
+ executeUidFrozenStateCallback(
+ new int[] {TEST_CALLING_UID, TEST_CALLING_UID_2},
+ new int[] {UID_FROZEN_STATE_FROZEN, UID_FROZEN_STATE_UNFROZEN});
+ assertEquals(7, mService.mAlarmStore.size());
+
+ executeUidFrozenStateCallback(
+ new int[] {TEST_CALLING_UID_2}, new int[] {UID_FROZEN_STATE_FROZEN});
+ assertEquals(6, mService.mAlarmStore.size());
+ }
+
+ @EnableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS)
+ @Test
+ public void alarmCountOnListenerFrozen() {
+ mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true);
+
+ // Set some alarms for TEST_CALLING_UID.
+ final int numExactListenerUid1 = 17;
+ for (int i = 0; i < numExactListenerUid1; i++) {
+ setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i,
+ getNewListener(() -> {}));
+ }
+ setTestAlarmWithListener(RTC, 42, getNewListener(() -> {}), 56, TEST_CALLING_UID);
+ setTestAlarm(ELAPSED_REALTIME, 54, getNewMockPendingIntent());
+ setTestAlarm(RTC, 49, 154, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null);
+
+ // Set some alarms for TEST_CALLING_UID_2.
+ final int numExactListenerUid2 = 11;
+ for (int i = 0; i < numExactListenerUid2; i++) {
+ setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i,
+ getNewListener(() -> {}), WINDOW_EXACT, TEST_CALLING_UID_2);
+ }
+ setTestAlarmWithListener(RTC, 412, getNewListener(() -> {}), 561, TEST_CALLING_UID_2);
+ setTestAlarm(RTC_WAKEUP, 26, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
+ TEST_CALLING_UID_2, null);
+
+ assertEquals(numExactListenerUid1 + 3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
+
+ executeUidFrozenStateCallback(
+ new int[] {TEST_CALLING_UID, TEST_CALLING_UID_2},
+ new int[] {UID_FROZEN_STATE_FROZEN, UID_FROZEN_STATE_UNFROZEN});
+ assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
+
+ executeUidFrozenStateCallback(
+ new int[] {TEST_CALLING_UID_2}, new int[] {UID_FROZEN_STATE_FROZEN});
+ assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
assertEquals(2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
index 940469f89b16..414532b88e22 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
@@ -29,16 +29,20 @@ import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupTransport;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.os.Build;
import android.os.Message;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.modules.utils.testing.TestableDeviceConfig;
+import com.android.server.backup.Flags;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.BackupHandler;
import com.android.server.backup.transport.BackupTransportClient;
@@ -56,10 +60,12 @@ import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
@@ -74,10 +80,17 @@ public class PerformUnifiedRestoreTaskTest {
private static final String SYSTEM_PACKAGE_NAME = "android";
private static final String NON_SYSTEM_PACKAGE_NAME = "package";
- @Mock private BackupDataInput mBackupDataInput;
- @Mock private BackupDataOutput mBackupDataOutput;
- @Mock private UserBackupManagerService mBackupManagerService;
- @Mock private TransportConnection mTransportConnection;
+ private static final String V_TO_U_ALLOWLIST = "pkg1";
+ private static final String V_TO_U_DENYLIST = "pkg2";
+
+ @Mock
+ private BackupDataInput mBackupDataInput;
+ @Mock
+ private BackupDataOutput mBackupDataOutput;
+ @Mock
+ private UserBackupManagerService mBackupManagerService;
+ @Mock
+ private TransportConnection mTransportConnection;
private Set<String> mExcludedkeys = new HashSet<>();
private Map<String, String> mBackupData = new HashMap<>();
@@ -91,6 +104,10 @@ public class PerformUnifiedRestoreTaskTest {
public TestableDeviceConfig.TestableDeviceConfigRule mDeviceConfigRule =
new TestableDeviceConfig.TestableDeviceConfigRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+
private Context mContext;
@Before
@@ -118,7 +135,8 @@ public class PerformUnifiedRestoreTaskTest {
return null;
});
- mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService, mTransportConnection);
+ mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService, mTransportConnection,
+ V_TO_U_ALLOWLIST, V_TO_U_DENYLIST);
}
private void populateTestData() {
@@ -235,6 +253,122 @@ public class PerformUnifiedRestoreTaskTest {
== UnifiedRestoreState.FINAL);
}
+ @Test
+ public void testCreateVToUList_listSettingIsNull_returnEmptyList() {
+ List<String> expectedEmptyList = new ArrayList<>();
+
+ List<String> list = mRestoreTask.createVToUList(null);
+
+ assertEquals(list, expectedEmptyList);
+ }
+
+ @Test
+ public void testCreateVToUList_listIsNotNull_returnCorrectList() {
+ List<String> expectedList = Arrays.asList("a", "b", "c");
+ String listString = "a,b,c";
+
+ List<String> list = mRestoreTask.createVToUList(listString);
+
+ assertEquals(list, expectedList);
+ }
+
+ @Test
+ public void testIsVToUDowngrade_vToUFlagIsOffAndTargetIsUSourceIsV_returnFalse() {
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(
+ Build.VERSION_CODES.VANILLA_ICE_CREAM, Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+
+ assertFalse(isVToUDowngrade);
+ }
+
+ @Test
+ public void testIsVToUDowngrade_vToUFlagIsOnAndTargetIsUSourceIsV_returnTrue() {
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(
+ Build.VERSION_CODES.VANILLA_ICE_CREAM, Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+
+ assertTrue(isVToUDowngrade);
+ }
+
+ @Test
+ public void testIsVToUDowngrade_vToUFlagIsOnAndSourceIsNotV_returnFalse() {
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+
+ assertFalse(isVToUDowngrade);
+ }
+
+ @Test
+ public void testIsVToUDowngrade_vToUFlagIsOnAndTargetIsNotU_returnFalse() {
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(
+ Build.VERSION_CODES.VANILLA_ICE_CREAM, Build.VERSION_CODES.VANILLA_ICE_CREAM);
+
+ assertFalse(isVToUDowngrade);
+ }
+
+
+ @Test
+ public void testIsEligibleForVToUDowngrade_pkgIsNotOnAllowlist_returnFalse() {
+ PackageInfo testPackageInfo = new PackageInfo();
+ testPackageInfo.packageName = "pkg";
+ testPackageInfo.applicationInfo = new ApplicationInfo();
+ // restoreAnyVersion flag is off
+ testPackageInfo.applicationInfo.flags = 0;
+
+ boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+ assertFalse(eligibilityCriteria);
+ }
+
+ @Test
+ public void testIsEligibleForVToUDowngrade_pkgIsOnAllowlist_returnTrue() {
+ PackageInfo testPackageInfo = new PackageInfo();
+ testPackageInfo.packageName = "pkg1";
+ testPackageInfo.applicationInfo = new ApplicationInfo();
+ // restoreAnyVersion flag is off
+ testPackageInfo.applicationInfo.flags = 0;
+
+ boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+ assertTrue(eligibilityCriteria);
+ }
+
+ @Test
+ public void testIsEligibleForVToUDowngrade_pkgIsNotOnDenyList_returnTrue() {
+ PackageInfo testPackageInfo = new PackageInfo();
+ testPackageInfo.packageName = "pkg";
+ testPackageInfo.applicationInfo = new ApplicationInfo();
+ // restoreAnyVersion flag is on
+ testPackageInfo.applicationInfo.flags = ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
+
+ boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+ assertTrue(eligibilityCriteria);
+ }
+
+ @Test
+ public void testIsEligibleForVToUDowngrade_pkgIsOnDenyList_returnFalse() {
+ PackageInfo testPackageInfo = new PackageInfo();
+ testPackageInfo.packageName = "pkg2";
+ testPackageInfo.applicationInfo = new ApplicationInfo();
+ // restoreAnyVersion flag is on
+ testPackageInfo.applicationInfo.flags = ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
+
+ boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+ assertFalse(eligibilityCriteria);
+ }
+
private void setupForRestoreKeyValueState(int transportStatus)
throws RemoteException, TransportNotAvailableException {
// Mock BackupHandler to do nothing when executeNextState() is called
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
new file mode 100644
index 000000000000..e42bdad97730
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "postsubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.rollback"
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index 6cd79bc09fb6..ae6984ef0a8d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -18,12 +18,17 @@ package com.android.server.power.stats;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.content.Context;
import android.hardware.SensorManager;
+import android.os.AggregateBatteryConsumer;
import android.os.BatteryConsumer;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -34,6 +39,7 @@ import android.os.Parcel;
import android.os.Process;
import android.os.UidBatteryConsumer;
import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.SparseLongArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -517,6 +523,57 @@ public class BatteryUsageStatsProviderTest {
}
@Test
+ public void saveBatteryUsageStatsOnReset_incompatibleEnergyConsumers() throws Throwable {
+ MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ batteryStats.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
+ int componentId0 = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+ int componentId1 = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1;
+
+ synchronized (batteryStats) {
+ batteryStats.getUidStatsLocked(APP_UID);
+
+ SparseLongArray uidEnergies = new SparseLongArray();
+ uidEnergies.put(APP_UID, 30_000_000);
+ batteryStats.updateCustomEnergyConsumerStatsLocked(0, 100_000_000, uidEnergies);
+ batteryStats.updateCustomEnergyConsumerStatsLocked(1, 200_000_000, uidEnergies);
+ }
+
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
+
+ PowerStatsStore powerStatsStore = mock(PowerStatsStore.class);
+ doAnswer(invocation -> {
+ BatteryUsageStats stats = invocation.getArgument(1);
+ AggregateBatteryConsumer device = stats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+ assertThat(device.getCustomPowerComponentName(componentId0)).isEqualTo("FOO");
+ assertThat(device.getCustomPowerComponentName(componentId1)).isEqualTo("BAR");
+ assertThat(device.getConsumedPowerForCustomComponent(componentId0))
+ .isWithin(PRECISION).of(27.77777);
+ assertThat(device.getConsumedPowerForCustomComponent(componentId1))
+ .isWithin(PRECISION).of(55.55555);
+
+ UidBatteryConsumer uid = stats.getUidBatteryConsumers().get(0);
+ assertThat(uid.getConsumedPowerForCustomComponent(componentId0))
+ .isWithin(PRECISION).of(8.33333);
+ assertThat(uid.getConsumedPowerForCustomComponent(componentId1))
+ .isWithin(PRECISION).of(8.33333);
+ return null;
+ }).when(powerStatsStore).storeBatteryUsageStats(anyLong(), any());
+
+ mStatsRule.getBatteryStats().saveBatteryUsageStatsOnReset(provider, powerStatsStore);
+
+ // Make an incompatible change of supported energy components. This will trigger
+ // a BatteryStats reset, which will generate a snapshot of battery stats.
+ mStatsRule.initMeasuredEnergyStatsLocked(
+ new String[]{"COMPONENT1"});
+
+ mStatsRule.waitForBackgroundThread();
+
+ verify(powerStatsStore).storeBatteryUsageStats(anyLong(), any());
+ }
+
+ @Test
public void testAggregateBatteryStats_incompatibleSnapshot() {
MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
batteryStats.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index 8bdb0292bf00..7e8fa55020ab 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -16,6 +16,8 @@
package com.android.server.power.stats;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -28,6 +30,7 @@ import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.UidBatteryConsumer;
@@ -74,6 +77,7 @@ public class BatteryUsageStatsRule implements TestRule {
private NetworkStats mNetworkStats;
private boolean[] mSupportedStandardBuckets;
private String[] mCustomPowerComponentNames;
+ private Throwable mThrowable;
public BatteryUsageStatsRule() {
this(0, null);
@@ -270,6 +274,7 @@ public class BatteryUsageStatsRule implements TestRule {
public void evaluate() throws Throwable {
before();
base.evaluate();
+ after();
}
};
}
@@ -277,6 +282,9 @@ public class BatteryUsageStatsRule implements TestRule {
private void before() {
lateInitBatteryStats();
HandlerThread bgThread = new HandlerThread("bg thread");
+ bgThread.setUncaughtExceptionHandler((thread, throwable)-> {
+ mThrowable = throwable;
+ });
bgThread.start();
mHandler = new Handler(bgThread.getLooper());
mBatteryStats.setHandler(mHandler);
@@ -285,6 +293,26 @@ public class BatteryUsageStatsRule implements TestRule {
mBatteryStats.getOnBatteryScreenOffTimeBase().setRunning(!mScreenOn, 0, 0);
}
+ private void after() throws Throwable {
+ if (mHandler != null) {
+ waitForBackgroundThread();
+ }
+ }
+
+ public void waitForBackgroundThread() throws Throwable {
+ if (mThrowable != null) {
+ throw mThrowable;
+ }
+
+ ConditionVariable done = new ConditionVariable();
+ mHandler.post(done::open);
+ assertThat(done.block(10000)).isTrue();
+
+ if (mThrowable != null) {
+ throw mThrowable;
+ }
+ }
+
public PowerProfile getPowerProfile() {
return mPowerProfile;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt
index 52c7d8d2bd2e..5c8c6bb69ef8 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt
@@ -17,31 +17,38 @@ package com.android.server.accessibility
import android.hardware.display.DisplayManagerGlobal
import android.os.SystemClock
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.DisplayAdjustments
import android.view.DisplayInfo
import android.view.IInputFilterHost
+import android.view.InputDevice.SOURCE_STYLUS
import android.view.InputDevice.SOURCE_TOUCHSCREEN
import android.view.InputEvent
import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_CANCEL
import android.view.MotionEvent.ACTION_DOWN
-import android.view.MotionEvent.ACTION_MOVE
-import android.view.MotionEvent.ACTION_UP
import android.view.MotionEvent.ACTION_HOVER_ENTER
import android.view.MotionEvent.ACTION_HOVER_EXIT
import android.view.MotionEvent.ACTION_HOVER_MOVE
+import android.view.MotionEvent.ACTION_MOVE
+import android.view.MotionEvent.ACTION_UP
import android.view.WindowManagerPolicyConstants.FLAG_PASS_TO_USER
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.cts.input.inputeventmatchers.withDeviceId
import com.android.cts.input.inputeventmatchers.withMotionAction
+import com.android.cts.input.inputeventmatchers.withSource
import com.android.server.LocalServices
import com.android.server.accessibility.magnification.MagnificationProcessor
import com.android.server.wm.WindowManagerInternal
import java.util.concurrent.LinkedBlockingQueue
import org.hamcrest.Matchers.allOf
import org.junit.After
+import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -92,12 +99,17 @@ class AccessibilityInputFilterInputTest {
or AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER
or AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS
or AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS)
+ const val STYLUS_SOURCE = SOURCE_STYLUS or SOURCE_TOUCHSCREEN
}
@Rule
@JvmField
val mocks: MockitoRule = MockitoJUnit.rule()
+ @Rule
+ @JvmField
+ val mCheckFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
@Mock
private lateinit var mockA11yController: WindowManagerInternal.AccessibilityControllerInternal
@@ -115,6 +127,9 @@ class AccessibilityInputFilterInputTest {
private lateinit var ams: AccessibilityManagerService
private lateinit var a11yInputFilter: AccessibilityInputFilter
private val touchDeviceId = 1
+ private val fromTouchScreen = allOf(withDeviceId(touchDeviceId), withSource(SOURCE_TOUCHSCREEN))
+ private val stylusDeviceId = 2
+ private val fromStylus = allOf(withDeviceId(stylusDeviceId), withSource(STYLUS_SOURCE))
@Before
fun setUp() {
@@ -156,23 +171,14 @@ class AccessibilityInputFilterInputTest {
enableFeatures(0)
val downTime = SystemClock.uptimeMillis()
- val downEvent = createMotionEvent(
- ACTION_DOWN, downTime, downTime, SOURCE_TOUCHSCREEN, touchDeviceId)
- send(downEvent)
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_DOWN), withDeviceId(touchDeviceId)))
-
- val moveEvent = createMotionEvent(
- ACTION_MOVE, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId)
- send(moveEvent)
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_MOVE), withDeviceId(touchDeviceId)))
-
- val upEvent = createMotionEvent(
- ACTION_UP, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId)
- send(upEvent)
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_UP), withDeviceId(touchDeviceId)))
+ sendTouchEvent(ACTION_DOWN, downTime, downTime)
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_DOWN)))
+
+ sendTouchEvent(ACTION_MOVE, downTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_MOVE)))
+
+ sendTouchEvent(ACTION_UP, downTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_UP)))
verifier.assertNoEvents()
}
@@ -186,28 +192,91 @@ class AccessibilityInputFilterInputTest {
enableFeatures(ALL_A11Y_FEATURES)
val downTime = SystemClock.uptimeMillis()
- val downEvent = createMotionEvent(
- ACTION_DOWN, downTime, downTime, SOURCE_TOUCHSCREEN, touchDeviceId)
- send(MotionEvent.obtain(downEvent))
-
+ sendTouchEvent(ACTION_DOWN, downTime, downTime)
// DOWN event gets transformed to HOVER_ENTER
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_HOVER_ENTER), withDeviceId(touchDeviceId)))
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
// MOVE becomes HOVER_MOVE
- val moveEvent = createMotionEvent(
- ACTION_MOVE, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId)
- send(moveEvent)
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_HOVER_MOVE), withDeviceId(touchDeviceId)))
+ sendTouchEvent(ACTION_MOVE, downTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_MOVE)))
// UP becomes HOVER_EXIT
- val upEvent = createMotionEvent(
- ACTION_UP, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId)
- send(upEvent)
+ sendTouchEvent(ACTION_UP, downTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_EXIT)))
+
+ verifier.assertNoEvents()
+ }
+
+ /**
+ * Enable all a11y features and send a touchscreen stream of DOWN -> CANCEL -> DOWN events.
+ * These get converted into HOVER_ENTER -> HOVER_EXIT -> HOVER_ENTER events by the input filter.
+ */
+ @Test
+ fun testTouchDownCancelDownWithAllA11yFeatures() {
+ enableFeatures(ALL_A11Y_FEATURES)
+
+ val downTime = SystemClock.uptimeMillis()
+ sendTouchEvent(ACTION_DOWN, downTime, downTime)
+ // DOWN event gets transformed to HOVER_ENTER
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
+
+ // CANCEL becomes HOVER_EXIT
+ sendTouchEvent(ACTION_CANCEL, downTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_EXIT)))
+
+ // DOWN again! New hover is expected
+ val newDownTime = SystemClock.uptimeMillis()
+ sendTouchEvent(ACTION_DOWN, newDownTime, newDownTime)
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
+
+ verifier.assertNoEvents()
+ }
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_HOVER_EXIT), withDeviceId(touchDeviceId)))
+ /**
+ * Enable all a11y features and send a stylus stream of DOWN -> CANCEL -> DOWN events.
+ * These get converted into HOVER_ENTER -> HOVER_EXIT -> HOVER_ENTER events by the input filter.
+ * This test is the same as above, but for stylus events.
+ */
+ @Test
+ fun testStylusDownCancelDownWithAllA11yFeatures() {
+ enableFeatures(ALL_A11Y_FEATURES)
+
+ val downTime = SystemClock.uptimeMillis()
+ sendStylusEvent(ACTION_DOWN, downTime, downTime)
+ // DOWN event gets transformed to HOVER_ENTER
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_ENTER)))
+
+ // CANCEL becomes HOVER_EXIT
+ sendStylusEvent(ACTION_CANCEL, downTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_EXIT)))
+
+ // DOWN again! New hover is expected
+ val newDownTime = SystemClock.uptimeMillis()
+ sendStylusEvent(ACTION_DOWN, newDownTime, newDownTime)
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_ENTER)))
+
+ verifier.assertNoEvents()
+ }
+
+ /**
+ * Enable all a11y features and send a stylus stream and then a touch stream.
+ */
+ @Test
+ fun testStylusThenTouch() {
+ enableFeatures(ALL_A11Y_FEATURES)
+
+ val downTime = SystemClock.uptimeMillis()
+ sendStylusEvent(ACTION_DOWN, downTime, downTime)
+ // DOWN event gets transformed to HOVER_ENTER
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_ENTER)))
+
+ // CANCEL becomes HOVER_EXIT
+ sendStylusEvent(ACTION_CANCEL, downTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_EXIT)))
+
+ val newDownTime = SystemClock.uptimeMillis()
+ sendTouchEvent(ACTION_DOWN, newDownTime, newDownTime)
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
verifier.assertNoEvents()
}
@@ -223,26 +292,18 @@ class AccessibilityInputFilterInputTest {
enableFeatures(ALL_A11Y_FEATURES)
val downTime = SystemClock.uptimeMillis()
- val downEvent = createMotionEvent(
- ACTION_DOWN, downTime, downTime, SOURCE_TOUCHSCREEN, touchDeviceId)
- send(MotionEvent.obtain(downEvent))
+ sendTouchEvent(ACTION_DOWN, downTime, downTime)
// DOWN event gets transformed to HOVER_ENTER
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_HOVER_ENTER), withDeviceId(touchDeviceId)))
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
verifier.assertNoEvents()
enableFeatures(0)
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_HOVER_EXIT), withDeviceId(touchDeviceId)))
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_EXIT)))
verifier.assertNoEvents()
- val moveEvent = createMotionEvent(
- ACTION_MOVE, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId)
- send(moveEvent)
- val upEvent = createMotionEvent(
- ACTION_UP, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId)
- send(upEvent)
+ sendTouchEvent(ACTION_MOVE, downTime, SystemClock.uptimeMillis())
+ sendTouchEvent(ACTION_UP, downTime, SystemClock.uptimeMillis())
// As the original gesture continues, no additional events should be getting sent by the
// filter because the HOVER_EXIT above already effectively finished the current gesture and
// the DOWN event was never sent to the host.
@@ -250,10 +311,148 @@ class AccessibilityInputFilterInputTest {
// Bug: the down event was swallowed, so the remainder of the gesture should be swallowed
// too. However, the MOVE and UP events are currently passed back to the dispatcher.
// TODO(b/310014874) - ensure a11y sends consistent input streams to the dispatcher
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_MOVE), withDeviceId(touchDeviceId)))
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_UP), withDeviceId(touchDeviceId)))
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_MOVE)))
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_UP)))
+
+ verifier.assertNoEvents()
+ }
+
+ /**
+ * Check multi-device behaviour when all a11y features are disabled. The events should pass
+ * through unmodified, but only from the active (first) device.
+ * The events from the inactive device should be dropped.
+ * In this test, we are injecting a touchscreen event stream and a stylus event stream,
+ * interleaved.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_HANDLE_MULTI_DEVICE_INPUT)
+ fun testMultiDeviceEventsWithoutA11yFeatures() {
+ enableFeatures(0)
+
+ val touchDownTime = SystemClock.uptimeMillis()
+
+ // Touch device - ACTION_DOWN
+ sendTouchEvent(ACTION_DOWN, touchDownTime, touchDownTime)
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_DOWN)))
+
+ // Stylus device - ACTION_DOWN
+ val stylusDownTime = SystemClock.uptimeMillis()
+ sendStylusEvent(ACTION_DOWN, stylusDownTime, stylusDownTime)
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_CANCEL)))
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_DOWN)))
+
+ // Touch device - ACTION_MOVE
+ sendTouchEvent(ACTION_MOVE, touchDownTime, SystemClock.uptimeMillis())
+ // Touch event is dropped
+ verifier.assertNoEvents()
+
+ // Stylus device - ACTION_MOVE
+ sendStylusEvent(ACTION_MOVE, stylusDownTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_MOVE)))
+
+ // Touch device - ACTION_UP
+ sendTouchEvent(ACTION_UP, touchDownTime, SystemClock.uptimeMillis())
+ // Touch event is dropped
+ verifier.assertNoEvents()
+
+ // Stylus device - ACTION_UP
+ sendStylusEvent(ACTION_UP, stylusDownTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_UP)))
+
+ verifier.assertNoEvents()
+ }
+
+ /**
+ * Check multi-device behaviour when all a11y features are enabled. The events should be
+ * modified accordingly, like DOWN events getting converted to hovers.
+ * Only a single device should be active (the latest device to start a new gesture).
+ * In this test, we are injecting a touchscreen event stream and a stylus event stream,
+ * interleaved.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_HANDLE_MULTI_DEVICE_INPUT)
+ fun testMultiDeviceEventsWithAllA11yFeatures() {
+ enableFeatures(ALL_A11Y_FEATURES)
+
+ // Touch device - ACTION_DOWN
+ val touchDownTime = SystemClock.uptimeMillis()
+ sendTouchEvent(ACTION_DOWN, touchDownTime, touchDownTime)
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
+
+ // Stylus device - ACTION_DOWN
+ val stylusDownTime = SystemClock.uptimeMillis()
+ sendStylusEvent(ACTION_DOWN, stylusDownTime, stylusDownTime)
+ // Touch is canceled and stylus is started
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_EXIT)))
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_ENTER)))
+
+ // Touch device - ACTION_MOVE
+ sendTouchEvent(ACTION_MOVE, touchDownTime, SystemClock.uptimeMillis())
+ // Stylus is active now; touch is ignored
+ verifier.assertNoEvents()
+
+ // Stylus device - ACTION_MOVE
+ sendStylusEvent(ACTION_MOVE, stylusDownTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_MOVE)))
+
+ // Touch device - ACTION_UP
+ sendTouchEvent(ACTION_UP, touchDownTime, SystemClock.uptimeMillis())
+ // Stylus is still active; touch is ignored
+ verifier.assertNoEvents()
+
+ sendStylusEvent(ACTION_UP, stylusDownTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_EXIT)))
+
+ // Now stylus is done, and a new touch gesture will work!
+ val newTouchDownTime = SystemClock.uptimeMillis()
+ sendTouchEvent(ACTION_DOWN, newTouchDownTime, newTouchDownTime)
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
+
+ verifier.assertNoEvents()
+ }
+
+ /**
+ * Check multi-device behaviour when all a11y features are enabled. The events should be
+ * modified accordingly, like DOWN events getting converted to hovers.
+ * Only a single device should be active at a given time. The touch events start and end
+ * while stylus is active. Check that the latest device is always given preference.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_HANDLE_MULTI_DEVICE_INPUT)
+ fun testStylusWithTouchInTheMiddle() {
+ enableFeatures(ALL_A11Y_FEATURES)
+
+ // Stylus device - ACTION_DOWN
+ val stylusDownTime = SystemClock.uptimeMillis()
+ sendStylusEvent(ACTION_DOWN, stylusDownTime, stylusDownTime)
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_ENTER)))
+
+ // Touch device - ACTION_DOWN
+ val touchDownTime = SystemClock.uptimeMillis()
+ sendTouchEvent(ACTION_DOWN, touchDownTime, touchDownTime)
+ // Touch DOWN causes stylus to get canceled
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_EXIT)))
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
+
+ // Touch device - ACTION_MOVE
+ sendTouchEvent(ACTION_MOVE, touchDownTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_MOVE)))
+
+ sendStylusEvent(ACTION_MOVE, stylusDownTime, SystemClock.uptimeMillis())
+ // Stylus is ignored because touch is active now
+ verifier.assertNoEvents()
+
+ sendTouchEvent(ACTION_UP, touchDownTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_EXIT)))
+
+ sendStylusEvent(ACTION_UP, stylusDownTime, SystemClock.uptimeMillis())
+ // The UP stylus event is also ignored
+ verifier.assertNoEvents()
+
+ // Now stylus works again, because touch gesture is finished
+ val newStylusDownTime = SystemClock.uptimeMillis()
+ sendStylusEvent(ACTION_DOWN, newStylusDownTime, newStylusDownTime)
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_ENTER)))
verifier.assertNoEvents()
}
@@ -264,6 +463,20 @@ class AccessibilityInputFilterInputTest {
return display
}
+ private fun sendTouchEvent(action: Int, downTime: Long, eventTime: Long) {
+ if (action == ACTION_DOWN) {
+ assertEquals(downTime, eventTime)
+ }
+ send(createMotionEvent(action, downTime, eventTime, SOURCE_TOUCHSCREEN, touchDeviceId))
+ }
+
+ private fun sendStylusEvent(action: Int, downTime: Long, eventTime: Long) {
+ if (action == ACTION_DOWN) {
+ assertEquals(downTime, eventTime)
+ }
+ send(createMotionEvent(action, downTime, eventTime, STYLUS_SOURCE, stylusDeviceId))
+ }
+
private fun send(event: InputEvent) {
// We need to make a copy of the event before sending it to the filter, because the filter
// will recycle it, but the caller of this function might want to still be able to use
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java
index b322dd709c2d..aec3f451fac6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -33,17 +34,24 @@ import android.testing.DexmakerShareClassLoaderRule;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.util.HexDump;
+
import com.google.common.truth.Expect;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.io.File;
import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
/**
@@ -51,184 +59,265 @@ import java.util.List;
*
* <p>Prefer adding new tests in CTS where possible.
*/
+@RunWith(Enclosed.class)
public class BrailleDisplayConnectionTest {
- private static final Path NULL_PATH = Path.of("/dev/null");
-
- private BrailleDisplayConnection mBrailleDisplayConnection;
- @Mock
- private BrailleDisplayConnection.NativeInterface mNativeInterface;
- @Mock
- private AccessibilityServiceConnection mServiceConnection;
-
- @Rule
- public final Expect expect = Expect.create();
-
- private Context mContext;
-
- // To mock package-private class
- @Rule
- public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
- new DexmakerShareClassLoaderRule();
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getInstrumentation().getContext();
- when(mServiceConnection.isConnectedLocked()).thenReturn(true);
- mBrailleDisplayConnection =
- spy(new BrailleDisplayConnection(new Object(), mServiceConnection));
- }
- @Test
- public void defaultNativeScanner_getHidrawNodePaths_returnsHidrawPaths() throws Exception {
- File testDir = mContext.getFilesDir();
- Path hidrawNode0 = Path.of(testDir.getPath(), "hidraw0");
- Path hidrawNode1 = Path.of(testDir.getPath(), "hidraw1");
- Path otherDevice = Path.of(testDir.getPath(), "otherDevice");
- Path[] nodePaths = {hidrawNode0, hidrawNode1, otherDevice};
- try {
- for (Path node : nodePaths) {
- assertThat(node.toFile().createNewFile()).isTrue();
+ public static class ScannerTest {
+ private static final Path NULL_PATH = Path.of("/dev/null");
+
+ private BrailleDisplayConnection mBrailleDisplayConnection;
+ @Mock
+ private BrailleDisplayConnection.NativeInterface mNativeInterface;
+ @Mock
+ private AccessibilityServiceConnection mServiceConnection;
+
+ @Rule
+ public final Expect expect = Expect.create();
+
+ private Context mContext;
+
+ // To mock package-private class
+ @Rule
+ public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+ new DexmakerShareClassLoaderRule();
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ when(mServiceConnection.isConnectedLocked()).thenReturn(true);
+ mBrailleDisplayConnection =
+ spy(new BrailleDisplayConnection(new Object(), mServiceConnection));
+ }
+
+ @Test
+ public void defaultNativeScanner_getHidrawNodePaths_returnsHidrawPaths() throws Exception {
+ File testDir = mContext.getFilesDir();
+ Path hidrawNode0 = Path.of(testDir.getPath(), "hidraw0");
+ Path hidrawNode1 = Path.of(testDir.getPath(), "hidraw1");
+ Path otherDevice = Path.of(testDir.getPath(), "otherDevice");
+ Path[] nodePaths = {hidrawNode0, hidrawNode1, otherDevice};
+ try {
+ for (Path node : nodePaths) {
+ assertThat(node.toFile().createNewFile()).isTrue();
+ }
+
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+
+ assertThat(scanner.getHidrawNodePaths(testDir.toPath()))
+ .containsExactly(hidrawNode0, hidrawNode1);
+ } finally {
+ for (Path node : nodePaths) {
+ node.toFile().delete();
+ }
}
+ }
+
+ @Test
+ public void defaultNativeScanner_getReportDescriptor_returnsDescriptor() {
+ int descriptorSize = 4;
+ byte[] descriptor = {0xB, 0xE, 0xE, 0xF};
+ when(mNativeInterface.getHidrawDescSize(anyInt())).thenReturn(descriptorSize);
+ when(mNativeInterface.getHidrawDesc(anyInt(), eq(descriptorSize))).thenReturn(
+ descriptor);
BrailleDisplayConnection.BrailleDisplayScanner scanner =
mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
- assertThat(scanner.getHidrawNodePaths(testDir.toPath()))
- .containsExactly(hidrawNode0, hidrawNode1);
- } finally {
- for (Path node : nodePaths) {
- node.toFile().delete();
- }
+ assertThat(scanner.getDeviceReportDescriptor(NULL_PATH)).isEqualTo(descriptor);
}
- }
- @Test
- public void defaultNativeScanner_getReportDescriptor_returnsDescriptor() {
- int descriptorSize = 4;
- byte[] descriptor = {0xB, 0xE, 0xE, 0xF};
- when(mNativeInterface.getHidrawDescSize(anyInt())).thenReturn(descriptorSize);
- when(mNativeInterface.getHidrawDesc(anyInt(), eq(descriptorSize))).thenReturn(descriptor);
+ @Test
+ public void defaultNativeScanner_getReportDescriptor_invalidSize_returnsNull() {
+ when(mNativeInterface.getHidrawDescSize(anyInt())).thenReturn(0);
- BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
- assertThat(scanner.getDeviceReportDescriptor(NULL_PATH)).isEqualTo(descriptor);
- }
+ assertThat(scanner.getDeviceReportDescriptor(NULL_PATH)).isNull();
+ }
- @Test
- public void defaultNativeScanner_getReportDescriptor_invalidSize_returnsNull() {
- when(mNativeInterface.getHidrawDescSize(anyInt())).thenReturn(0);
+ @Test
+ public void defaultNativeScanner_getUniqueId_returnsUniq() {
+ String macAddress = "12:34:56:78";
+ when(mNativeInterface.getHidrawUniq(anyInt())).thenReturn(macAddress);
- BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
- assertThat(scanner.getDeviceReportDescriptor(NULL_PATH)).isNull();
- }
+ assertThat(scanner.getUniqueId(NULL_PATH)).isEqualTo(macAddress);
+ }
+
+ @Test
+ public void defaultNativeScanner_getDeviceBusType_busUsb() {
+ when(mNativeInterface.getHidrawBusType(anyInt()))
+ .thenReturn(BrailleDisplayConnection.BUS_USB);
- @Test
- public void defaultNativeScanner_getUniqueId_returnsUniq() {
- String macAddress = "12:34:56:78";
- when(mNativeInterface.getHidrawUniq(anyInt())).thenReturn(macAddress);
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
- BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ assertThat(scanner.getDeviceBusType(NULL_PATH))
+ .isEqualTo(BrailleDisplayConnection.BUS_USB);
+ }
- assertThat(scanner.getUniqueId(NULL_PATH)).isEqualTo(macAddress);
- }
+ @Test
+ public void defaultNativeScanner_getDeviceBusType_busBluetooth() {
+ when(mNativeInterface.getHidrawBusType(anyInt()))
+ .thenReturn(BrailleDisplayConnection.BUS_BLUETOOTH);
- @Test
- public void defaultNativeScanner_getDeviceBusType_busUsb() {
- when(mNativeInterface.getHidrawBusType(anyInt()))
- .thenReturn(BrailleDisplayConnection.BUS_USB);
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
- BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ assertThat(scanner.getDeviceBusType(NULL_PATH))
+ .isEqualTo(BrailleDisplayConnection.BUS_BLUETOOTH);
+ }
- assertThat(scanner.getDeviceBusType(NULL_PATH))
- .isEqualTo(BrailleDisplayConnection.BUS_USB);
- }
+ @Test
+ public void write_bypassesServiceSideCheckWithLargeBuffer_disconnects() {
+ Mockito.doNothing().when(mBrailleDisplayConnection).disconnect();
+ mBrailleDisplayConnection.write(
+ new byte[IBinder.getSuggestedMaxIpcSizeBytes() * 2]);
- @Test
- public void defaultNativeScanner_getDeviceBusType_busBluetooth() {
- when(mNativeInterface.getHidrawBusType(anyInt()))
- .thenReturn(BrailleDisplayConnection.BUS_BLUETOOTH);
+ verify(mBrailleDisplayConnection).disconnect();
+ }
- BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ @Test
+ public void write_notConnected_throwsIllegalStateException() {
+ when(mServiceConnection.isConnectedLocked()).thenReturn(false);
- assertThat(scanner.getDeviceBusType(NULL_PATH))
- .isEqualTo(BrailleDisplayConnection.BUS_BLUETOOTH);
- }
+ assertThrows(IllegalStateException.class,
+ () -> mBrailleDisplayConnection.write(new byte[1]));
+ }
- @Test
- public void write_bypassesServiceSideCheckWithLargeBuffer_disconnects() {
- Mockito.doNothing().when(mBrailleDisplayConnection).disconnect();
- mBrailleDisplayConnection.write(
- new byte[IBinder.getSuggestedMaxIpcSizeBytes() * 2]);
+ @Test
+ public void write_unableToCreateWriteStream_disconnects() {
+ Mockito.doNothing().when(mBrailleDisplayConnection).disconnect();
+ // mBrailleDisplayConnection#connectLocked was never called so the
+ // connection's mHidrawNode is still null. This will throw an exception
+ // when attempting to create FileOutputStream on the node.
+ mBrailleDisplayConnection.write(new byte[1]);
- verify(mBrailleDisplayConnection).disconnect();
- }
+ verify(mBrailleDisplayConnection).disconnect();
+ }
- @Test
- public void write_notConnected_throwsIllegalStateException() {
- when(mServiceConnection.isConnectedLocked()).thenReturn(false);
+ // BrailleDisplayConnection#setTestData() is used to enable CTS testing with
+ // test Braille display data, but its own implementation should also be tested
+ // so that issues in this helper don't cause confusing failures in CTS.
+
+ @Test
+ public void setTestData_scannerReturnsTestData() {
+ Bundle bd1 = new Bundle(), bd2 = new Bundle();
+
+ Path path1 = Path.of("/dev/path1"), path2 = Path.of("/dev/path2");
+ bd1.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_HIDRAW_PATH,
+ path1.toString());
+ bd2.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_HIDRAW_PATH,
+ path2.toString());
+ byte[] desc1 = {0xB, 0xE}, desc2 = {0xE, 0xF};
+ bd1.putByteArray(BrailleDisplayController.TEST_BRAILLE_DISPLAY_DESCRIPTOR, desc1);
+ bd2.putByteArray(BrailleDisplayController.TEST_BRAILLE_DISPLAY_DESCRIPTOR, desc2);
+ String uniq1 = "uniq1", uniq2 = "uniq2";
+ bd1.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_UNIQUE_ID, uniq1);
+ bd2.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_UNIQUE_ID, uniq2);
+ int bus1 = BrailleDisplayConnection.BUS_USB, bus2 =
+ BrailleDisplayConnection.BUS_BLUETOOTH;
+ bd1.putBoolean(BrailleDisplayController.TEST_BRAILLE_DISPLAY_BUS_BLUETOOTH,
+ bus1 == BrailleDisplayConnection.BUS_BLUETOOTH);
+ bd2.putBoolean(BrailleDisplayController.TEST_BRAILLE_DISPLAY_BUS_BLUETOOTH,
+ bus2 == BrailleDisplayConnection.BUS_BLUETOOTH);
- assertThrows(IllegalStateException.class,
- () -> mBrailleDisplayConnection.write(new byte[1]));
- }
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.setTestData(List.of(bd1, bd2));
+
+ expect.that(scanner.getHidrawNodePaths(Path.of("/dev"))).containsExactly(path1, path2);
+ expect.that(scanner.getDeviceReportDescriptor(path1)).isEqualTo(desc1);
+ expect.that(scanner.getDeviceReportDescriptor(path2)).isEqualTo(desc2);
+ expect.that(scanner.getUniqueId(path1)).isEqualTo(uniq1);
+ expect.that(scanner.getUniqueId(path2)).isEqualTo(uniq2);
+ expect.that(scanner.getDeviceBusType(path1)).isEqualTo(bus1);
+ expect.that(scanner.getDeviceBusType(path2)).isEqualTo(bus2);
+ }
- @Test
- public void write_unableToCreateWriteStream_disconnects() {
- Mockito.doNothing().when(mBrailleDisplayConnection).disconnect();
- // mBrailleDisplayConnection#connectLocked was never called so the
- // connection's mHidrawNode is still null. This will throw an exception
- // when attempting to create FileOutputStream on the node.
- mBrailleDisplayConnection.write(new byte[1]);
+ @Test
+ public void setTestData_emptyTestData_returnsNullNodePaths() {
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.setTestData(List.of());
- verify(mBrailleDisplayConnection).disconnect();
+ expect.that(scanner.getHidrawNodePaths(Path.of("/dev"))).isNull();
+ }
}
- // BrailleDisplayConnection#setTestData() is used to enable CTS testing with
- // test Braille display data, but its own implementation should also be tested
- // so that issues in this helper don't cause confusing failures in CTS.
-
- @Test
- public void setTestData_scannerReturnsTestData() {
- Bundle bd1 = new Bundle(), bd2 = new Bundle();
-
- Path path1 = Path.of("/dev/path1"), path2 = Path.of("/dev/path2");
- bd1.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_HIDRAW_PATH, path1.toString());
- bd2.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_HIDRAW_PATH, path2.toString());
- byte[] desc1 = {0xB, 0xE}, desc2 = {0xE, 0xF};
- bd1.putByteArray(BrailleDisplayController.TEST_BRAILLE_DISPLAY_DESCRIPTOR, desc1);
- bd2.putByteArray(BrailleDisplayController.TEST_BRAILLE_DISPLAY_DESCRIPTOR, desc2);
- String uniq1 = "uniq1", uniq2 = "uniq2";
- bd1.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_UNIQUE_ID, uniq1);
- bd2.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_UNIQUE_ID, uniq2);
- int bus1 = BrailleDisplayConnection.BUS_USB, bus2 = BrailleDisplayConnection.BUS_BLUETOOTH;
- bd1.putBoolean(BrailleDisplayController.TEST_BRAILLE_DISPLAY_BUS_BLUETOOTH,
- bus1 == BrailleDisplayConnection.BUS_BLUETOOTH);
- bd2.putBoolean(BrailleDisplayController.TEST_BRAILLE_DISPLAY_BUS_BLUETOOTH,
- bus2 == BrailleDisplayConnection.BUS_BLUETOOTH);
-
- BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.setTestData(List.of(bd1, bd2));
-
- expect.that(scanner.getHidrawNodePaths(Path.of("/dev"))).containsExactly(path1, path2);
- expect.that(scanner.getDeviceReportDescriptor(path1)).isEqualTo(desc1);
- expect.that(scanner.getDeviceReportDescriptor(path2)).isEqualTo(desc2);
- expect.that(scanner.getUniqueId(path1)).isEqualTo(uniq1);
- expect.that(scanner.getUniqueId(path2)).isEqualTo(uniq2);
- expect.that(scanner.getDeviceBusType(path1)).isEqualTo(bus1);
- expect.that(scanner.getDeviceBusType(path2)).isEqualTo(bus2);
- }
+ @RunWith(Parameterized.class)
+ public static class BrailleDisplayDescriptorTest {
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][]{
+ {"match_BdPage", new byte[]{
+ // Just one item, defines the BD page
+ 0x05, 0x41}},
+ {"match_BdPageAfterAnotherPage", new byte[]{
+ // One item defines another page
+ 0x05, 0x01,
+ // Next item defines BD page
+ 0x05, 0x41}},
+ {"match_BdPageAfterSizeZeroItem", new byte[]{
+ // Size-zero item (last 2 bits are 00)
+ 0x00,
+ // Next item defines BD page
+ 0x05, 0x41}},
+ {"match_BdPageAfterSizeOneItem", new byte[]{
+ // Size-one item (last 2 bits are 01)
+ 0x01, 0x7F,
+ // Next item defines BD page
+ 0x05, 0x41}},
+ {"match_BdPageAfterSizeTwoItem", new byte[]{
+ // Size-two item (last 2 bits are 10)
+ 0x02, 0x7F, 0x7F,
+ 0x05, 0x41}},
+ {"match_BdPageAfterSizeFourItem", new byte[]{
+ // Size-four item (last 2 bits are 11)
+ 0x03, 0x7F, 0x7F, 0x7F, 0x7F,
+ 0x05, 0x41}},
+ {"match_BdPageInBetweenOtherPages", new byte[]{
+ // One item defines another page
+ 0x05, 0x01,
+ // Next item defines BD page
+ 0x05, 0x41,
+ // Next item defines another page
+ 0x05, 0x02}},
+ {"fail_OtherPage", new byte[]{
+ // Just one item, defines another page
+ 0x05, 0x01}},
+ {"fail_BdPageBeforeMissingData", new byte[]{
+ // This item defines BD page
+ 0x05, 0x41,
+ // Next item specifies size-one item (last 2 bits are 01) but
+ // that one data byte is missing; this descriptor is malformed.
+ 0x01}},
+ {"fail_BdPageWithWrongDataSize", new byte[]{
+ // This item defines a page with two-byte ID 0x41 0x7F, not 0x41.
+ 0x06, 0x41, 0x7F}},
+ {"fail_LongItem", new byte[]{
+ // Item has type bits 1111, indicating Long Item.
+ (byte) 0xF0}},
+ });
+ }
- @Test
- public void setTestData_emptyTestData_returnsNullNodePaths() {
- BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.setTestData(List.of());
- expect.that(scanner.getHidrawNodePaths(Path.of("/dev"))).isNull();
+ @Parameterized.Parameter(0)
+ public String mTestName;
+ @Parameterized.Parameter(1)
+ public byte[] mDescriptor;
+
+ @Test
+ public void isBrailleDisplay() {
+ final boolean expectedMatch = mTestName.startsWith("match_");
+ assertWithMessage(
+ "Expected isBrailleDisplay==" + expectedMatch
+ + " for descriptor " + HexDump.toHexString(mDescriptor))
+ .that(BrailleDisplayConnection.isBrailleDisplay(mDescriptor))
+ .isEqualTo(expectedMatch);
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricNotificationLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricNotificationLoggerTest.java
new file mode 100644
index 000000000000..8319623eec0a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricNotificationLoggerTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.platform.test.annotations.Presubmit;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+
+import com.android.server.biometrics.log.BiometricFrameworkStatsLogger;
+import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class BiometricNotificationLoggerTest {
+ @Rule
+ public MockitoRule mockitorule = MockitoJUnit.rule();
+
+ @Mock
+ private BiometricFrameworkStatsLogger mLogger;
+ private BiometricNotificationLogger mNotificationLogger;
+
+ @Before
+ public void setUp() {
+ mNotificationLogger = new BiometricNotificationLogger(
+ mLogger);
+ }
+
+ @Test
+ public void testNotification_nullNotification_doesNothing() {
+ mNotificationLogger.onNotificationPosted(null, null);
+
+ verify(mLogger, never()).logFrameworkNotification(anyInt(), anyInt());
+ }
+
+ @Test
+ public void testNotification_emptyStringTag_doesNothing() {
+ final StatusBarNotification noti = createNotificationWithNullTag();
+ mNotificationLogger.onNotificationPosted(noti, null);
+
+ verify(mLogger, never()).logFrameworkNotification(anyInt(), anyInt());
+ }
+
+ @Test
+ public void testFaceNotification_posted() {
+ final StatusBarNotification noti = createFaceNotification();
+ mNotificationLogger.onNotificationPosted(noti, null);
+
+ verify(mLogger).logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_SHOWN,
+ BiometricsProtoEnums.MODALITY_FACE);
+ }
+
+ @Test
+ public void testFingerprintNotification_posted() {
+ final StatusBarNotification noti = createFingerprintNotification();
+ mNotificationLogger.onNotificationPosted(noti, null);
+
+ verify(mLogger).logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_SHOWN,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ }
+
+ @Test
+ public void testFaceNotification_clicked() {
+ final StatusBarNotification noti = createFaceNotification();
+ mNotificationLogger.onNotificationRemoved(noti, null,
+ NotificationListenerService.REASON_CLICK);
+
+ verify(mLogger).logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_CLICKED,
+ BiometricsProtoEnums.MODALITY_FACE);
+ }
+
+ @Test
+ public void testFingerprintNotification_clicked() {
+ final StatusBarNotification noti = createFingerprintNotification();
+ mNotificationLogger.onNotificationRemoved(noti, null,
+ NotificationListenerService.REASON_CLICK);
+
+ verify(mLogger).logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_CLICKED,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ }
+
+ @Test
+ public void testFaceNotification_dismissed() {
+ final StatusBarNotification noti = createFaceNotification();
+ mNotificationLogger.onNotificationRemoved(noti, null,
+ NotificationListenerService.REASON_CANCEL);
+
+ verify(mLogger).logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_DISMISSED,
+ BiometricsProtoEnums.MODALITY_FACE);
+ }
+
+ @Test
+ public void testFingerprintNotification_dismissed() {
+ final StatusBarNotification noti = createFingerprintNotification();
+ mNotificationLogger.onNotificationRemoved(noti, null,
+ NotificationListenerService.REASON_CANCEL);
+
+ verify(mLogger).logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_DISMISSED,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ }
+
+ private StatusBarNotification createNotificationWithNullTag() {
+ final StatusBarNotification notification = mock(StatusBarNotification.class);
+ return notification;
+ }
+
+ private StatusBarNotification createFaceNotification() {
+ final StatusBarNotification notification = mock(StatusBarNotification.class);
+ when(notification.getTag())
+ .thenReturn(BiometricNotificationUtils.FACE_ENROLL_NOTIFICATION_TAG);
+ return notification;
+ }
+
+ private StatusBarNotification createFingerprintNotification() {
+ final StatusBarNotification notification = mock(StatusBarNotification.class);
+ when(notification.getTag())
+ .thenReturn(BiometricNotificationUtils.FINGERPRINT_ENROLL_NOTIFICATION_TAG);
+ return notification;
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 35ad55c0938e..49583ef5194b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -187,6 +187,9 @@ public class BiometricServiceTest {
@Mock
private IGateKeeperService mGateKeeperService;
+ @Mock
+ private BiometricNotificationLogger mNotificationLogger;
+
BiometricContextProvider mBiometricContextProvider;
@Before
@@ -242,6 +245,7 @@ public class BiometricServiceTest {
when(mInjector.getBiometricContext(any())).thenReturn(mBiometricContextProvider);
when(mInjector.getKeystoreAuthorizationService()).thenReturn(mKeystoreAuthService);
when(mInjector.getGateKeeperService()).thenReturn(mGateKeeperService);
+ when(mInjector.getNotificationLogger()).thenReturn(mNotificationLogger);
when(mGateKeeperService.getSecureUserId(anyInt())).thenReturn(42L);
if (com.android.server.biometrics.Flags.deHidl()) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
index b69b55448bcd..238a9289c05b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -137,11 +137,11 @@ public class BiometricLoggerTest {
final long latency = 44;
final boolean enrollSuccessful = true;
- mLogger.logOnEnrolled(targetUserId, latency, enrollSuccessful);
+ mLogger.logOnEnrolled(targetUserId, latency, enrollSuccessful, -1);
verify(mSink).enroll(
eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT),
- eq(targetUserId), eq(latency), eq(enrollSuccessful), anyFloat());
+ eq(targetUserId), eq(latency), eq(enrollSuccessful), anyFloat(), anyInt());
}
@Test
@@ -192,7 +192,8 @@ public class BiometricLoggerTest {
true/* isBiometricPrompt */);
mLogger.logOnEnrolled(2 /* targetUserId */,
10 /* latency */,
- true /* enrollSuccessful */);
+ true /* enrollSuccessful */,
+ 30 /* source */);
mLogger.logOnError(mContext, mOpContext,
4 /* error */,
0 /* vendorCode */,
@@ -205,7 +206,8 @@ public class BiometricLoggerTest {
anyInt(), anyInt(), anyInt(), anyBoolean(),
anyLong(), anyInt(), anyBoolean(), anyInt(), anyFloat());
verify(mSink, never()).enroll(
- anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyBoolean(), anyFloat());
+ anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyBoolean(), anyFloat(),
+ anyInt());
verify(mSink, never()).error(eq(mOpContext),
anyInt(), anyInt(), anyInt(), anyBoolean(),
anyLong(), anyInt(), anyInt(), anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index f7480dea780b..981eba55a430 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -1139,7 +1139,7 @@ public class BiometricSchedulerTest {
super(context, lazyDaemon, token, listener, 0 /* userId */, new byte[69],
"test" /* owner */, mock(BiometricUtils.class), 5 /* timeoutSec */,
TEST_SENSOR_ID, true /* shouldVibrate */, mock(BiometricLogger.class),
- mock(BiometricContext.class));
+ mock(BiometricContext.class), 0 /* enrollReason */);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
index 43ed07a0bf76..02363cd66349 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
@@ -21,15 +21,20 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyByte;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
+import android.hardware.face.FaceEnrollOptions;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -68,6 +73,7 @@ public class FaceEnrollClientTest {
private static final byte[] HAT = new byte[69];
private static final int USER_ID = 12;
+ private static final int ENROLL_SOURCE = FaceEnrollOptions.ENROLL_REASON_SUW;
@Rule
public final TestableContext mContext = new TestableContext(
@@ -208,6 +214,16 @@ public class FaceEnrollClientTest {
verify(mHal).enrollWithOptions(any());
}
+ @Test
+ public void testEnrollWithReasonLogsMetric() throws RemoteException {
+ final FaceEnrollClient client = createClient(4);
+ client.start(mCallback);
+ client.onEnrollResult(new Face("face", 1 /* faceId */, 20 /* deviceId */), 0);
+
+ verify(mBiometricLogger).logOnEnrolled(anyInt(), anyLong(), anyBoolean(),
+ eq(BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW));
+ }
+
private FaceEnrollClient createClient() throws RemoteException {
return createClient(200 /* version */);
}
@@ -221,6 +237,7 @@ public class FaceEnrollClientTest {
mUtils, new int[0] /* disabledFeatures */, 6 /* timeoutSec */,
null /* previewSurface */, 8 /* sensorId */,
mBiometricLogger, mBiometricContext, 5 /* maxTemplatesPerUser */,
- true /* debugConsent */);
+ true /* debugConsent */,
+ (new FaceEnrollOptions.Builder()).setEnrollReason(ENROLL_SOURCE).build());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
index 940fe69925b5..7a778d5dd211 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
@@ -34,6 +34,7 @@ import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.OptionalUint64;
import android.hardware.biometrics.face.V1_0.Status;
import android.hardware.face.Face;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.HidlFaceSensorConfig;
import android.os.Handler;
import android.os.RemoteException;
@@ -201,7 +202,7 @@ public class HidlToAidlSensorAdapterTest {
USER_ID, HAT, TAG, 1 /* requestId */, mBiometricUtils,
new int[]{} /* disabledFeatures */, ENROLL_TIMEOUT_SEC, null /* previewSurface */,
SENSOR_ID, mLogger, mBiometricContext, 1 /* maxTemplatesPerUser */,
- false /* debugConsent */));
+ false /* debugConsent */, (new FaceEnrollOptions.Builder()).build()));
mLooper.dispatchAll();
verify(mAidlResponseHandlerCallback).onEnrollSuccess();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index 3ee54f53d44f..916f6960efc9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -21,6 +21,7 @@ import static com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.inOrder;
@@ -30,10 +31,12 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
@@ -78,6 +81,8 @@ import java.util.function.Consumer;
@SmallTest
public class FingerprintEnrollClientTest {
+ private static final int ENROLL_SOURCE = FingerprintEnrollOptions.ENROLL_REASON_SUW;
+
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Rule
public final CheckFlagsRule mCheckFlagsRule =
@@ -353,6 +358,16 @@ public class FingerprintEnrollClientTest {
c -> c.onEnrollResult(new Fingerprint("", 1, 1), 0));
}
+ @Test
+ public void testEnrollWithReasonLogsMetric() throws RemoteException {
+ final FingerprintEnrollClient client = createClient(4);
+ client.start(mCallback);
+ client.onEnrollResult(new Fingerprint("fingerprint", 1 /* faceId */, 20 /* deviceId */), 0);
+
+ verify(mBiometricLogger).logOnEnrolled(anyInt(), anyLong(), anyBoolean(),
+ eq(BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW));
+ }
+
private void showHideOverlay_sidefpsControllerRemovalRefactor(
Consumer<FingerprintEnrollClient> block) throws RemoteException {
mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR);
@@ -382,6 +397,8 @@ public class FingerprintEnrollClientTest {
HAT, "owner", mBiometricUtils, 8 /* sensorId */,
mBiometricLogger, mBiometricContext, mSensorProps, mUdfpsOverlayController,
mSideFpsController, mAuthenticationStateListeners, 6 /* maxTemplatesPerUser */,
- FingerprintManager.ENROLL_ENROLL);
+ FingerprintManager.ENROLL_ENROLL, (new FingerprintEnrollOptions.Builder())
+ .setEnrollReason(ENROLL_SOURCE).build()
+ );
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java
index cbbc54547bf6..6c3bfe80510e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java
@@ -34,6 +34,7 @@ import android.app.AlarmManager;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.HidlFingerprintSensorConfig;
import android.os.Handler;
import android.os.HandlerThread;
@@ -217,7 +218,8 @@ public class HidlToAidlSensorAdapterTest {
1 /* requestId */, null /* listener */, USER_ID, HAT, TAG, mBiometricUtils,
SENSOR_ID, mLogger, mBiometricContext,
mHidlToAidlSensorAdapter.getSensorProperties(), null, null,
- mAuthenticationStateListeners, 5 /* maxTemplatesPerUser */, ENROLL_ENROLL));
+ mAuthenticationStateListeners, 5 /* maxTemplatesPerUser */, ENROLL_ENROLL,
+ (new FingerprintEnrollOptions.Builder()).build()));
mLooper.dispatchAll();
verify(mAidlResponseHandlerCallback).onEnrollSuccess();
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 81df597f3f33..3e748ffb37e9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -75,6 +75,7 @@ import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.UserInfo;
import android.content.pm.UserPackage;
+import android.content.pm.UserProperties;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Icon;
@@ -776,6 +777,15 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
new UserInfo(USER_P1, "userP1",
UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_MANAGED_PROFILE), 0);
+ protected static final UserProperties USER_PROPERTIES_0 =
+ new UserProperties.Builder().setItemsRestrictedOnHomeScreen(false).build();
+
+ protected static final UserProperties USER_PROPERTIES_10 =
+ new UserProperties.Builder().setItemsRestrictedOnHomeScreen(false).build();
+
+ protected static final UserProperties USER_PROPERTIES_11 =
+ new UserProperties.Builder().setItemsRestrictedOnHomeScreen(true).build();
+
protected BiPredicate<String, Integer> mDefaultLauncherChecker =
(callingPackage, userId) ->
LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage)
@@ -817,6 +827,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
= new HashMap<>();
protected final Map<Integer, UserInfo> mUserInfos = new HashMap<>();
+ protected final Map<Integer, UserProperties> mUserProperties = new HashMap<>();
protected final Map<Integer, Boolean> mRunningUsers = new HashMap<>();
protected final Map<Integer, Boolean> mUnlockedUsers = new HashMap<>();
@@ -911,6 +922,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
mUserInfos.put(USER_11, USER_INFO_11);
mUserInfos.put(USER_P0, USER_INFO_P0);
mUserInfos.put(USER_P1, USER_INFO_P1);
+ mUserProperties.put(USER_0, USER_PROPERTIES_0);
+ mUserProperties.put(USER_10, USER_PROPERTIES_10);
+ mUserProperties.put(USER_11, USER_PROPERTIES_11);
when(mMockUserManagerInternal.isUserUnlockingOrUnlocked(anyInt()))
.thenAnswer(inv -> {
@@ -959,6 +973,15 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
inv -> mUserInfos.get((Integer) inv.getArguments()[0])));
when(mMockActivityManagerInternal.getUidProcessState(anyInt())).thenReturn(
ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+ when(mMockUserManagerInternal.getUserProperties(anyInt()))
+ .thenAnswer(inv -> {
+ final int userId = (Integer) inv.getArguments()[0];
+ final UserProperties userProperties = mUserProperties.get(userId);
+ if (userProperties == null) {
+ return new UserProperties.Builder().build();
+ }
+ return userProperties;
+ });
// User 0 and P0 are always running
mRunningUsers.put(USER_0, true);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index f1d3ba9db489..1331ae173b18 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -197,8 +197,6 @@ public class UserManagerServiceUserPropertiesTest {
assertEqualGetterOrThrows(orig::getAlwaysVisible, copy::getAlwaysVisible, exposeAll);
assertEqualGetterOrThrows(orig::getAllowStoppingUserWithDelayedLocking,
copy::getAllowStoppingUserWithDelayedLocking, exposeAll);
- assertEqualGetterOrThrows(orig::areItemsRestrictedOnHomeScreen,
- copy::areItemsRestrictedOnHomeScreen, exposeAll);
// Items requiring hasManagePermission - put them here using hasManagePermission.
assertEqualGetterOrThrows(orig::getShowInSettings, copy::getShowInSettings,
@@ -220,6 +218,8 @@ public class UserManagerServiceUserPropertiesTest {
copy::getCrossProfileContentSharingStrategy, true);
assertEqualGetterOrThrows(orig::getProfileApiVisibility, copy::getProfileApiVisibility,
true);
+ assertEqualGetterOrThrows(orig::areItemsRestrictedOnHomeScreen,
+ copy::areItemsRestrictedOnHomeScreen, true);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 7f7cc35ced55..44c464ed6adf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -350,8 +350,8 @@ public final class UserManagerTest {
privateProfileUserProperties::getAllowStoppingUserWithDelayedLocking);
assertThat(typeProps.getProfileApiVisibility()).isEqualTo(
privateProfileUserProperties.getProfileApiVisibility());
- assertThrows(SecurityException.class,
- privateProfileUserProperties::areItemsRestrictedOnHomeScreen);
+ assertThat(typeProps.areItemsRestrictedOnHomeScreen())
+ .isEqualTo(privateProfileUserProperties.areItemsRestrictedOnHomeScreen());
compareDrawables(mUserManager.getUserBadge(),
Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain()));
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index dfda1fc9dd65..3bc089fb3f5d 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -662,7 +662,7 @@ public class SystemConfigTest {
android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
public void getEnhancedConfirmationTrustedInstallers_returnsTrustedInstallers()
throws IOException {
- String pkgName = "com.example.app";
+ String packageName = "com.example.app";
String certificateDigestStr = "E9:7A:BC:2C:D1:CA:8D:58:6A:57:0B:8C:F8:60:AA:D2:"
+ "8D:13:30:2A:FB:C9:00:2C:5D:53:B2:6C:09:A4:85:A0";
@@ -670,7 +670,7 @@ public class SystemConfigTest {
.toByteArray();
String contents = "<config>"
+ "<" + "enhanced-confirmation-trusted-installer" + " "
- + "package=\"" + pkgName + "\""
+ + "package=\"" + packageName + "\""
+ " sha256-cert-digest=\"" + certificateDigestStr + "\""
+ "/>"
+ "</config>";
@@ -684,10 +684,10 @@ public class SystemConfigTest {
assertThat(actualTrustedInstallers.size()).isEqualTo(1);
SignedPackage actual = actualTrustedInstallers.stream().findFirst().orElseThrow();
- SignedPackage expected = new SignedPackage(pkgName, certificateDigest);
+ SignedPackage expected = new SignedPackage(packageName, certificateDigest);
assertThat(actual.getCertificateDigest()).isEqualTo(expected.getCertificateDigest());
- assertThat(actual.getPkgName()).isEqualTo(expected.getPkgName());
+ assertThat(actual.getPackageName()).isEqualTo(expected.getPackageName());
assertThat(actual).isEqualTo(expected);
}
diff --git a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
index 6c085e085f4e..c8bef45af839 100644
--- a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
@@ -16,9 +16,7 @@
package com.android.server.utils;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
import android.os.Handler;
import android.os.Looper;
@@ -110,7 +108,7 @@ public class AnrTimerTest {
*/
TestArg[] messages(int expected) {
synchronized (mLock) {
- assertEquals(expected, mMessages.size());
+ assertThat(mMessages.size()).isEqualTo(expected);
return mMessages.toArray(new TestArg[expected]);
}
}
@@ -154,8 +152,8 @@ public class AnrTimerTest {
}
void validate(TestArg expected, TestArg actual) {
- assertEquals(expected, actual);
- assertEquals(actual.what, MSG_TIMEOUT);
+ assertThat(actual).isEqualTo(expected);
+ assertThat(actual.what).isEqualTo(MSG_TIMEOUT);
}
@Parameters(name = "featureEnabled={0}")
@@ -180,11 +178,11 @@ public class AnrTimerTest {
Helper helper = new Helper(1);
try (TestAnrTimer timer = new TestAnrTimer(helper)) {
// One-time check that the injector is working as expected.
- assertEquals(mEnabled, timer.serviceEnabled());
+ assertThat(mEnabled).isEqualTo(timer.serviceEnabled());
TestArg t = new TestArg(1, 1);
timer.start(t, 10);
// Delivery is immediate but occurs on a different thread.
- assertTrue(helper.await(5000));
+ assertThat(helper.await(5000)).isTrue();
TestArg[] result = helper.messages(1);
validate(t, result[0]);
}
@@ -201,10 +199,10 @@ public class AnrTimerTest {
TestArg t = new TestArg(1, 1);
timer.start(t, 10000);
// Briefly pause.
- assertFalse(helper.await(10));
+ assertThat(helper.await(10)).isFalse();
timer.start(t, 10);
// Delivery is immediate but occurs on a different thread.
- assertTrue(helper.await(5000));
+ assertThat(helper.await(5000)).isTrue();
TestArg[] result = helper.messages(1);
validate(t, result[0]);
}
@@ -221,7 +219,7 @@ public class AnrTimerTest {
TestArg t = new TestArg(1, 1);
timer.start(t, 0);
// Delivery is immediate but occurs on a different thread.
- assertTrue(helper.await(5000));
+ assertThat(helper.await(5000)).isTrue();
TestArg[] result = helper.messages(1);
validate(t, result[0]);
}
@@ -243,7 +241,7 @@ public class AnrTimerTest {
timer.start(t2, 60);
timer.start(t3, 40);
// Delivery is immediate but occurs on a different thread.
- assertTrue(helper.await(5000));
+ assertThat(helper.await(5000)).isTrue();
TestArg[] result = helper.messages(3);
validate(t3, result[0]);
validate(t1, result[1]);
@@ -269,7 +267,7 @@ public class AnrTimerTest {
x2.start(t2, 60);
x3.start(t3, 40);
// Delivery is immediate but occurs on a different thread.
- assertTrue(helper.await(5000));
+ assertThat(helper.await(5000)).isTrue();
TestArg[] result = helper.messages(3);
validate(t3, result[0]);
validate(t1, result[1]);
@@ -292,10 +290,10 @@ public class AnrTimerTest {
timer.start(t2, 60);
timer.start(t3, 40);
// Briefly pause.
- assertFalse(helper.await(10));
+ assertThat(helper.await(10)).isFalse();
timer.cancel(t1);
// Delivery is immediate but occurs on a different thread.
- assertTrue(helper.await(5000));
+ assertThat(helper.await(5000)).isTrue();
TestArg[] result = helper.messages(2);
validate(t3, result[0]);
validate(t2, result[1]);
@@ -319,7 +317,7 @@ public class AnrTimerTest {
@Test
public void testDumpOutput() throws Exception {
String r1 = getDumpOutput();
- assertEquals(false, r1.contains("timer:"));
+ assertThat(r1).doesNotContain("timer:");
Helper helper = new Helper(2);
TestArg t1 = new TestArg(1, 1);
@@ -332,12 +330,15 @@ public class AnrTimerTest {
String r2 = getDumpOutput();
// There are timers in the list if and only if the feature is enabled.
- final boolean expected = mEnabled;
- assertEquals(expected, r2.contains("timer:"));
+ if (mEnabled) {
+ assertThat(r2).contains("timer:");
+ } else {
+ assertThat(r2).doesNotContain("timer:");
+ }
}
String r3 = getDumpOutput();
- assertEquals(false, r3.contains("timer:"));
+ assertThat(r3).doesNotContain("timer:");
}
/**
@@ -351,7 +352,7 @@ public class AnrTimerTest {
if (!mEnabled) return;
String r1 = getDumpOutput();
- assertEquals(false, r1.contains("timer:"));
+ assertThat(r1).doesNotContain("timer:");
Helper helper = new Helper(2);
TestArg t1 = new TestArg(1, 1);
@@ -366,8 +367,11 @@ public class AnrTimerTest {
String r2 = getDumpOutput();
// There are timers in the list if and only if the feature is enabled.
- final boolean expected = mEnabled;
- assertEquals(expected, r2.contains("timer:"));
+ if (mEnabled) {
+ assertThat(r2).contains("timer:");
+ } else {
+ assertThat(r2).doesNotContain("timer:");
+ }
}
// Try to make finalizers run. The timer object above should be a candidate. Finalizers
@@ -382,7 +386,7 @@ public class AnrTimerTest {
}
// The timer was not explicitly closed but it should have been implicitly closed by GC.
- assertEquals(false, r3.contains("timer:"));
+ assertThat(r3).doesNotContain("timer:");
}
// TODO: [b/302724778] Remove manual JNI load
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 92dad2598538..cff7f460feb5 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -5842,6 +5842,30 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testStats_DirectReplyLifetimeExtendedPostsUpdate() throws Exception {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
+ waitForIdle();
+
+ assertThat(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied())
+ .isTrue();
+ // Checks that a post update is sent.
+ verify(mWorkerHandler, times(1))
+ .post(any(NotificationManagerService.PostNotificationRunnable.class));
+ ArgumentCaptor<NotificationRecord> captor =
+ ArgumentCaptor.forClass(NotificationRecord.class);
+ verify(mListeners, times(1)).prepareNotifyPostedLocked(captor.capture(), any(),
+ anyBoolean());
+ assertThat(captor.getValue().getNotification().flags
+ & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(
+ FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY);
+ }
+
+ @Test
public void testStats_updatedOnUserExpansion() throws Exception {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
@@ -8507,6 +8531,36 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testStats_SmartReplyAlreadyLifetimeExtendedPostsUpdate() throws Exception {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+ final int replyIndex = 2;
+ final String reply = "Hello";
+ final boolean modifiedBeforeSending = true;
+ final boolean generatedByAssistant = true;
+
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ r.setSuggestionsGeneratedByAssistant(generatedByAssistant);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationSmartReplySent(
+ r.getKey(), replyIndex, reply, NOTIFICATION_LOCATION_UNKNOWN,
+ modifiedBeforeSending);
+ waitForIdle();
+
+ // Checks that a post update is sent.
+ verify(mWorkerHandler, times(1))
+ .post(any(NotificationManagerService.PostNotificationRunnable.class));
+ ArgumentCaptor<NotificationRecord> captor =
+ ArgumentCaptor.forClass(NotificationRecord.class);
+ verify(mListeners, times(1)).prepareNotifyPostedLocked(captor.capture(), any(),
+ anyBoolean());
+ assertThat(captor.getValue().getNotification().flags
+ & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(
+ FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY);
+ }
+
+ @Test
public void testOnNotificationActionClick() {
final int actionIndex = 2;
final Notification.Action action =
@@ -8537,6 +8591,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final Notification.Action action =
new Notification.Action.Builder(null, "text", PendingIntent.getActivity(
mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE)).build();
+ final boolean generatedByAssistant = false;
+
// Creates a notification marked as being lifetime extended.
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
@@ -8550,6 +8606,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// The flag is removed, so the notification is no longer lifetime extended.
assertThat(r.getSbn().getNotification().flags
& FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(0);
+
+ // The record is sent out without the flag.
+ ArgumentCaptor<NotificationRecord> captor =
+ ArgumentCaptor.forClass(NotificationRecord.class);
+ verify(mAssistants, times(1)).notifyAssistantActionClicked(
+ captor.capture(), eq(action), eq(generatedByAssistant));
+ assertThat(captor.getValue().getNotification().flags
+ & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(0);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 3f2ccafe6425..8c50ef406ec6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -260,6 +260,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
AppOpsManager mAppOps;
TestableFlagResolver mTestFlagResolver = new TestableFlagResolver();
ZenModeEventLoggerFake mZenModeEventLogger;
+ private String mPkg;
@Before
public void setUp() throws PackageManager.NameNotFoundException {
@@ -270,7 +271,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mContentResolver = mContext.getContentResolver();
mResources = mock(Resources.class, withSettings()
.spiedInstance(mContext.getResources()));
- String pkg = mContext.getPackageName();
+ mPkg = mContext.getPackageName();
try {
when(mResources.getXml(R.xml.default_zen_mode_config)).thenReturn(
getDefaultConfigParser());
@@ -301,14 +302,14 @@ public class ZenModeHelperTest extends UiServiceTestCase {
when(mPackageManager.getPackageUidAsUser(eq(CUSTOM_PKG_NAME), anyInt()))
.thenReturn(CUSTOM_PKG_UID);
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
- new String[]{pkg});
+ new String[]{mPkg});
ApplicationInfo appInfoSpy = spy(new ApplicationInfo());
appInfoSpy.icon = ICON_RES_ID;
when(appInfoSpy.loadLabel(any())).thenReturn(CUSTOM_APP_LABEL);
when(mPackageManager.getApplicationInfo(eq(CUSTOM_PKG_NAME), anyInt()))
.thenReturn(appInfoSpy);
- when(mPackageManager.getApplicationInfo(eq(mContext.getPackageName()), anyInt()))
+ when(mPackageManager.getApplicationInfo(eq(mPkg), anyInt()))
.thenReturn(appInfoSpy);
mZenModeHelper.mPm = mPackageManager;
@@ -2512,16 +2513,16 @@ public class ZenModeHelperTest extends UiServiceTestCase {
scheduleInfo.endHour = 1;
Uri sharedUri = ZenModeConfig.toScheduleConditionId(scheduleInfo);
AutomaticZenRule zenRule = new AutomaticZenRule("name",
- new ComponentName("android", "ScheduleConditionProvider"),
+ new ComponentName(mPkg, "ScheduleConditionProvider"),
sharedUri,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule("android", zenRule,
+ String id = mZenModeHelper.addAutomaticZenRule(mPkg, zenRule,
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
- new ComponentName("android", "ScheduleConditionProvider"),
+ new ComponentName(mPkg, "ScheduleConditionProvider"),
sharedUri,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id2 = mZenModeHelper.addAutomaticZenRule("android", zenRule2,
+ String id2 = mZenModeHelper.addAutomaticZenRule(mPkg, zenRule2,
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
Condition condition = new Condition(sharedUri, "", STATE_TRUE);
@@ -2531,11 +2532,11 @@ public class ZenModeHelperTest extends UiServiceTestCase {
for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
if (rule.id.equals(id)) {
assertNotNull(rule.condition);
- assertTrue(rule.condition.state == STATE_TRUE);
+ assertEquals(STATE_TRUE, rule.condition.state);
}
if (rule.id.equals(id2)) {
assertNotNull(rule.condition);
- assertTrue(rule.condition.state == STATE_TRUE);
+ assertEquals(STATE_TRUE, rule.condition.state);
}
}
@@ -2546,11 +2547,11 @@ public class ZenModeHelperTest extends UiServiceTestCase {
for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
if (rule.id.equals(id)) {
assertNotNull(rule.condition);
- assertTrue(rule.condition.state == STATE_FALSE);
+ assertEquals(STATE_FALSE, rule.condition.state);
}
if (rule.id.equals(id2)) {
assertNotNull(rule.condition);
- assertTrue(rule.condition.state == STATE_FALSE);
+ assertEquals(STATE_FALSE, rule.condition.state);
}
}
}
@@ -3637,14 +3638,14 @@ public class ZenModeHelperTest extends UiServiceTestCase {
AutomaticZenRule bedtime = new AutomaticZenRule.Builder("Bedtime Mode (TM)", CONDITION_ID)
.setType(TYPE_BEDTIME)
.build();
- String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule("pkg", bedtime, UPDATE_ORIGIN_APP,
+ String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule(mPkg, bedtime, UPDATE_ORIGIN_APP,
"reason", CUSTOM_PKG_UID);
// Create immersive rule
AutomaticZenRule immersive = new AutomaticZenRule.Builder("Immersed", CONDITION_ID)
.setType(TYPE_IMMERSIVE)
.build();
- String immersiveId = mZenModeHelper.addAutomaticZenRule("pkg", immersive, UPDATE_ORIGIN_APP,
+ String immersiveId = mZenModeHelper.addAutomaticZenRule(mPkg, immersive, UPDATE_ORIGIN_APP,
"reason", CUSTOM_PKG_UID);
// Event 2: Activate bedtime rule
@@ -5434,6 +5435,48 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void setAutomaticZenRuleState_idForNotOwnedRule_ignored() {
+ // Assume existence of an other-package-owned rule that is currently ACTIVE.
+ assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
+ ZenRule otherRule = newZenRule("another.package", Instant.now(), null);
+ otherRule.zenMode = ZEN_MODE_ALARMS;
+ otherRule.condition = new Condition(otherRule.conditionId, "on", Condition.STATE_TRUE);
+ ZenModeConfig config = mZenModeHelper.mConfig.copy();
+ config.automaticRules.put("otherRule", otherRule);
+ mZenModeHelper.setConfig(config, null, UPDATE_ORIGIN_INIT, "", Process.SYSTEM_UID);
+ assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+
+ // Should be ignored.
+ mZenModeHelper.setAutomaticZenRuleState("otherRule",
+ new Condition(otherRule.conditionId, "off", Condition.STATE_FALSE),
+ UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+ assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void setAutomaticZenRuleState_conditionForNotOwnedRule_ignored() {
+ // Assume existence of an other-package-owned rule that is currently ACTIVE.
+ assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
+ ZenRule otherRule = newZenRule("another.package", Instant.now(), null);
+ otherRule.zenMode = ZEN_MODE_ALARMS;
+ otherRule.condition = new Condition(otherRule.conditionId, "on", Condition.STATE_TRUE);
+ ZenModeConfig config = mZenModeHelper.mConfig.copy();
+ config.automaticRules.put("otherRule", otherRule);
+ mZenModeHelper.setConfig(config, null, UPDATE_ORIGIN_INIT, "", Process.SYSTEM_UID);
+ assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+
+ // Should be ignored.
+ mZenModeHelper.setAutomaticZenRuleState(otherRule.conditionId,
+ new Condition(otherRule.conditionId, "off", Condition.STATE_FALSE),
+ UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+ assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+ }
+
+ @Test
@EnableFlags(android.app.Flags.FLAG_MODES_API)
public void testCallbacks_policy() throws Exception {
setupZenConfig();
@@ -5583,13 +5626,13 @@ public class ZenModeHelperTest extends UiServiceTestCase {
public void applyGlobalZenModeAsImplicitZenRule_modeOff_deactivatesImplicitRule() {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.mConfig.automaticRules.clear();
- mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(mPkg, CUSTOM_PKG_UID,
ZEN_MODE_IMPORTANT_INTERRUPTIONS);
assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
.isEqualTo(STATE_TRUE);
- mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(mPkg, CUSTOM_PKG_UID,
ZEN_MODE_OFF);
assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 7db707a42ff0..e7571ef47864 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -1421,6 +1421,7 @@ public class VibratorManagerServiceTest {
public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception {
int defaultNotificationIntensity =
mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_NOTIFICATION);
+ // This will scale up notification vibrations.
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
defaultNotificationIntensity < Vibrator.VIBRATION_INTENSITY_HIGH
? defaultNotificationIntensity + 1
@@ -1428,6 +1429,7 @@ public class VibratorManagerServiceTest {
int defaultTouchIntensity =
mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_TOUCH);
+ // This will scale down touch vibrations.
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW
? defaultTouchIntensity - 1
@@ -1482,6 +1484,42 @@ public class VibratorManagerServiceTest {
}
@Test
+ public void vibrate_withBypassScaleFlag_ignoresIntensitySettingsAndResolvesAmplitude()
+ throws Exception {
+ // Permission needed for bypassing user settings
+ grantPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+
+ int defaultTouchIntensity =
+ mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_TOUCH);
+ // This will scale down touch vibrations.
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW
+ ? defaultTouchIntensity - 1
+ : defaultTouchIntensity);
+
+ int defaultAmplitude = mContextSpy.getResources().getInteger(
+ com.android.internal.R.integer.config_defaultVibrationAmplitude);
+
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+
+ vibrateAndWaitUntilFinished(service,
+ VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE),
+ new VibrationAttributes.Builder()
+ .setUsage(VibrationAttributes.USAGE_TOUCH)
+ .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)
+ .build());
+
+ assertEquals(1, fakeVibrator.getAllEffectSegments().size());
+
+ assertEquals(defaultAmplitude / 255f, fakeVibrator.getAmplitudes().get(0), 1e-5);
+
+ cancelVibrate(service); // Clean up long-ish effect.
+ }
+
+ @Test
public void vibrate_withPowerModeChange_cancelVibrationIfNotAllowed() throws Exception {
mockVibrators(1, 2);
VibratorManagerService service = createSystemReadyService();
@@ -1879,6 +1917,9 @@ public class VibratorManagerServiceTest {
@Test
public void onExternalVibration_withBypassMuteAudioFlag_ignoresUserSettings() {
+ // Permission needed for bypassing user settings
+ grantPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
@@ -1892,12 +1933,12 @@ public class VibratorManagerServiceTest {
.build();
createSystemReadyService();
- int scale = mExternalVibratorService.onExternalVibrationStart(
- new ExternalVibration(UID, PACKAGE_NAME, audioAttrs,
- mock(IExternalVibrationController.class)));
+ ExternalVibration vib = new ExternalVibration(UID, PACKAGE_NAME, audioAttrs,
+ mock(IExternalVibrationController.class));
+ int scale = mExternalVibratorService.onExternalVibrationStart(vib);
assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
- createSystemReadyService();
+ mExternalVibratorService.onExternalVibrationStop(vib);
scale = mExternalVibratorService.onExternalVibrationStart(
new ExternalVibration(UID, PACKAGE_NAME, flaggedAudioAttrs,
mock(IExternalVibrationController.class)));
@@ -1912,7 +1953,6 @@ public class VibratorManagerServiceTest {
Vibrator.VIBRATION_INTENSITY_OFF);
AudioAttributes flaggedAudioAttrs = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_UNKNOWN)
- .setFlags(AudioAttributes.FLAG_BYPASS_MUTE)
.build();
createSystemReadyService();
diff --git a/services/tests/wmtests/AndroidTest.xml b/services/tests/wmtests/AndroidTest.xml
index 46e87dceb8d4..2512ee592ef3 100644
--- a/services/tests/wmtests/AndroidTest.xml
+++ b/services/tests/wmtests/AndroidTest.xml
@@ -34,4 +34,11 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="settings put secure immersive_mode_confirmations confirmed" />
</target_preparer>
+
+ <!-- Collect the dumped files for debugging -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/storage/emulated/0/ScreenshotTests" />
+ <option name="clean-up" value="true" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
</configuration>
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
index db241de246f6..0544b89d70fd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -23,7 +23,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.google.common.truth.Truth.assertThat;
+
import android.content.Intent;
+import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -48,6 +51,8 @@ public class ActivityStartControllerTests extends WindowTestsBase {
private Factory mFactory;
private ActivityStarter mStarter;
+ private static final String TEST_TYPE = "testType";
+
@Before
public void setUp() throws Exception {
mFactory = mock(Factory.class);
@@ -58,6 +63,28 @@ public class ActivityStartControllerTests extends WindowTestsBase {
}
/**
+ * Ensures that when an [Activity] is started in a [TaskFragment] the associated
+ * [ActivityStarter.Request] has the intent's resolved type correctly set.
+ */
+ @Test
+ public void testStartActivityInTaskFragment_setsActivityStarterRequestResolvedType() {
+ final Intent intent = new Intent();
+ intent.setType(TEST_TYPE);
+
+ mController.startActivityInTaskFragment(
+ mock(TaskFragment.class),
+ intent,
+ null /* activityOptions */,
+ null /* resultTo */ ,
+ Binder.getCallingPid(),
+ Binder.getCallingUid(),
+ null /* errorCallbackToken */
+ );
+
+ assertThat(mStarter.mRequest.resolvedType).isEqualTo(TEST_TYPE);
+ }
+
+ /**
* Ensures instances are recycled after execution.
*/
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 266a0d5c8f28..4c32a586ae78 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -67,7 +67,7 @@ import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
-import android.window.IUnhandledDragListener;
+import android.window.IGlobalDragListener;
import androidx.test.filters.SmallTest;
@@ -544,9 +544,9 @@ public class DragDropControllerTests extends WindowTestsBase {
public void testUnhandledDragListenerNotCalledForNormalDrags() throws RemoteException {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
- final IUnhandledDragListener listener = mock(IUnhandledDragListener.class);
+ final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
- mTarget.setUnhandledDragListener(listener);
+ mTarget.setGlobalDragListener(listener);
doDragAndDrop(0, ClipData.newPlainText("label", "Test"), 0, 0);
verify(listener, times(0)).onUnhandledDrop(any(), any());
}
@@ -555,9 +555,9 @@ public class DragDropControllerTests extends WindowTestsBase {
public void testUnhandledDragListenerReceivesUnhandledDropOverWindow() {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
- final IUnhandledDragListener listener = mock(IUnhandledDragListener.class);
+ final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
- mTarget.setUnhandledDragListener(listener);
+ mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
// Notify the unhandled drag listener
@@ -578,9 +578,9 @@ public class DragDropControllerTests extends WindowTestsBase {
public void testUnhandledDragListenerReceivesUnhandledDropOverNoValidWindow() {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
- final IUnhandledDragListener listener = mock(IUnhandledDragListener.class);
+ final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
- mTarget.setUnhandledDragListener(listener);
+ mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
// Notify the unhandled drag listener
@@ -600,9 +600,9 @@ public class DragDropControllerTests extends WindowTestsBase {
public void testUnhandledDragListenerCallbackTimeout() {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
- final IUnhandledDragListener listener = mock(IUnhandledDragListener.class);
+ final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
- mTarget.setUnhandledDragListener(listener);
+ mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
// Notify the unhandled drag listener
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
index 0fcae92fdbfc..280fe4c30739 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
@@ -16,12 +16,14 @@
package com.android.server.wm;
+import static android.server.wm.CtsWindowInfoUtils.waitForStableWindowGeometry;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.statusBars;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -43,6 +45,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.ServiceManager;
import android.platform.test.annotations.Presubmit;
+import android.server.wm.BuildUtils;
import android.view.IWindowManager;
import android.view.PointerIcon;
import android.view.SurfaceControl;
@@ -50,7 +53,6 @@ import android.view.cts.surfacevalidator.BitmapPixelChecker;
import android.view.cts.surfacevalidator.SaveBitmapHelper;
import android.window.ScreenCapture;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
-import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
@@ -74,6 +76,7 @@ import java.util.concurrent.TimeUnit;
@SmallTest
@Presubmit
public class ScreenshotTests {
+ private static final long WAIT_TIME_S = 5L * BuildUtils.HW_TIMEOUT_MULTIPLIER;
private static final int BUFFER_WIDTH = 100;
private static final int BUFFER_HEIGHT = 100;
@@ -119,32 +122,61 @@ public class ScreenshotTests {
canvas.drawColor(Color.RED);
buffer.unlockCanvasAndPost(canvas);
+ CountDownLatch countDownLatch = new CountDownLatch(1);
t.show(secureSC)
.setBuffer(secureSC, HardwareBuffer.createFromGraphicBuffer(buffer))
.setDataSpace(secureSC, DataSpace.DATASPACE_SRGB)
- .apply(true);
+ .addTransactionCommittedListener(Runnable::run, countDownLatch::countDown)
+ .apply();
+ assertTrue("Failed to wait for transaction to get committed",
+ countDownLatch.await(WAIT_TIME_S, TimeUnit.SECONDS));
+ assertTrue("Failed to wait for stable geometry",
+ waitForStableWindowGeometry(WAIT_TIME_S, TimeUnit.SECONDS));
ScreenCapture.LayerCaptureArgs args = new ScreenCapture.LayerCaptureArgs.Builder(secureSC)
.setCaptureSecureLayers(true)
.setChildrenOnly(false)
.build();
- ScreenCapture.ScreenshotHardwareBuffer hardwareBuffer = ScreenCapture.captureLayers(args);
- assertNotNull(hardwareBuffer);
- Bitmap screenshot = hardwareBuffer.asBitmap();
- assertNotNull(screenshot);
-
- Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
- screenshot.recycle();
-
- BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(Color.RED);
- Rect bounds = new Rect(0, 0, swBitmap.getWidth(), swBitmap.getHeight());
- int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
- int sizeOfBitmap = bounds.width() * bounds.height();
- boolean success = numMatchingPixels == sizeOfBitmap;
- swBitmap.recycle();
-
- assertTrue(success);
+ ScreenshotHardwareBuffer[] screenCapture = new ScreenshotHardwareBuffer[1];
+ Bitmap screenshot = null;
+ Bitmap swBitmap = null;
+ try {
+ CountDownLatch screenshotComplete = new CountDownLatch(1);
+ ScreenCapture.captureLayers(args, new ScreenCapture.ScreenCaptureListener(
+ (screenshotHardwareBuffer, result) -> {
+ if (result == 0) {
+ screenCapture[0] = screenshotHardwareBuffer;
+ }
+ screenshotComplete.countDown();
+ }));
+ assertTrue("Failed to wait for screen capture",
+ screenshotComplete.await(WAIT_TIME_S, TimeUnit.SECONDS));
+ assertNotNull("Screen capture buffer is null", screenCapture[0]);
+
+ screenshot = screenCapture[0].asBitmap();
+ assertNotNull("Screenshot from bitmap is null", screenshot);
+
+ swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
+
+ BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(Color.RED);
+ Rect bounds = new Rect(0, 0, swBitmap.getWidth(), swBitmap.getHeight());
+ int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
+ int sizeOfBitmap = bounds.width() * bounds.height();
+
+ assertEquals("numMatchingPixels=" + numMatchingPixels + " sizeOfBitmap=" + sizeOfBitmap,
+ sizeOfBitmap, numMatchingPixels);
+ } finally {
+ if (screenshot != null) {
+ screenshot.recycle();
+ }
+ if (swBitmap != null) {
+ swBitmap.recycle();
+ }
+ if (screenCapture[0].getHardwareBuffer() != null) {
+ screenCapture[0].getHardwareBuffer().close();
+ }
+ }
}
@Test
@@ -169,36 +201,65 @@ public class ScreenshotTests {
buffer.unlockCanvasAndPost(canvas);
Point point = mActivity.getPositionBelowStatusBar();
+ CountDownLatch countDownLatch = new CountDownLatch(1);
t.show(sc)
.setBuffer(sc, HardwareBuffer.createFromGraphicBuffer(buffer))
.setDataSpace(sc, DataSpace.DATASPACE_SRGB)
.setPosition(sc, point.x, point.y)
- .apply(true);
-
- SynchronousScreenCaptureListener syncScreenCapture =
- ScreenCapture.createSyncCaptureListener();
- windowManager.captureDisplay(DEFAULT_DISPLAY, null, syncScreenCapture);
- ScreenshotHardwareBuffer hardwareBuffer = syncScreenCapture.getBuffer();
- assertNotNull(hardwareBuffer);
-
- Bitmap screenshot = hardwareBuffer.asBitmap();
- assertNotNull(screenshot);
-
- Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
- screenshot.recycle();
-
- BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(Color.RED);
- Rect bounds = new Rect(point.x, point.y, BUFFER_WIDTH + point.x, BUFFER_HEIGHT + point.y);
- int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
- int pixelMatchSize = bounds.width() * bounds.height();
- boolean success = numMatchingPixels == pixelMatchSize;
-
- if (!success) {
- SaveBitmapHelper.saveBitmap(swBitmap, getClass(), mTestName, "failedImage");
+ .addTransactionCommittedListener(Runnable::run, countDownLatch::countDown)
+ .apply();
+
+ assertTrue("Failed to wait for transaction to get committed",
+ countDownLatch.await(WAIT_TIME_S, TimeUnit.SECONDS));
+ assertTrue("Failed to wait for stable geometry",
+ waitForStableWindowGeometry(WAIT_TIME_S, TimeUnit.SECONDS));
+
+ ScreenshotHardwareBuffer[] screenCapture = new ScreenshotHardwareBuffer[1];
+ Bitmap screenshot = null;
+ Bitmap swBitmap = null;
+ try {
+ CountDownLatch screenshotComplete = new CountDownLatch(1);
+ windowManager.captureDisplay(DEFAULT_DISPLAY, null,
+ new ScreenCapture.ScreenCaptureListener(
+ (screenshotHardwareBuffer, result) -> {
+ if (result == 0) {
+ screenCapture[0] = screenshotHardwareBuffer;
+ }
+ screenshotComplete.countDown();
+ }));
+ assertTrue("Failed to wait for screen capture",
+ screenshotComplete.await(WAIT_TIME_S, TimeUnit.SECONDS));
+ assertNotNull("Screen capture buffer is null", screenCapture[0]);
+
+ screenshot = screenCapture[0].asBitmap();
+ assertNotNull("Screenshot from bitmap is null", screenshot);
+
+ swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
+
+ BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(Color.RED);
+ Rect bounds = new Rect(point.x, point.y, BUFFER_WIDTH + point.x,
+ BUFFER_HEIGHT + point.y);
+ int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
+ int pixelMatchSize = bounds.width() * bounds.height();
+ boolean success = numMatchingPixels == pixelMatchSize;
+
+ if (!success) {
+ SaveBitmapHelper.saveBitmap(swBitmap, getClass(), mTestName, "failedImage");
+ }
+ assertTrue(
+ "numMatchingPixels=" + numMatchingPixels + " pixelMatchSize=" + pixelMatchSize,
+ success);
+ } finally {
+ if (screenshot != null) {
+ screenshot.recycle();
+ }
+ if (swBitmap != null) {
+ swBitmap.recycle();
+ }
+ if (screenCapture[0].getHardwareBuffer() != null) {
+ screenCapture[0].getHardwareBuffer().close();
+ }
}
- swBitmap.recycle();
- assertTrue("numMatchingPixels=" + numMatchingPixels + " pixelMatchSize=" + pixelMatchSize,
- success);
}
public static class ScreenshotActivity extends Activity {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java b/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java
index 298637266cc3..824b8d7f6727 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java
@@ -16,10 +16,18 @@
package com.android.server.wm;
+import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
+import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.os.Binder;
+import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
@@ -27,6 +35,7 @@ import androidx.test.filters.SmallTest;
import com.android.server.wm.SensitiveContentPackages.PackageInfo;
import org.junit.After;
+import org.junit.Rule;
import org.junit.Test;
import java.util.Set;
@@ -44,23 +53,30 @@ public class SensitiveContentPackagesTest {
private static final int APP_UID_1 = 5;
private static final int APP_UID_2 = 6;
- private static final int APP_UID_3 = 7;
+ private static final IBinder WINDOW_TOKEN_1 = new Binder();
+ private static final IBinder WINDOW_TOKEN_2 = new Binder();
private final SensitiveContentPackages mSensitiveContentPackages =
new SensitiveContentPackages();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@After
public void tearDown() {
mSensitiveContentPackages.clearBlockedApps();
}
+ private boolean shouldBlockScreenCaptureForApp(String pkg, int uid, IBinder windowToken) {
+ return mSensitiveContentPackages.shouldBlockScreenCaptureForApp(pkg, uid, windowToken);
+ }
+
@Test
- public void addBlockScreenCaptureForApps() {
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
+ public void shouldBlockScreenCaptureForNotificationApps() {
ArraySet<PackageInfo> blockedApps = new ArraySet(
Set.of(new PackageInfo(APP_PKG_1, APP_UID_1),
- new PackageInfo(APP_PKG_1, APP_UID_2),
- new PackageInfo(APP_PKG_2, APP_UID_1),
new PackageInfo(APP_PKG_2, APP_UID_2)
));
@@ -68,17 +84,50 @@ public class SensitiveContentPackagesTest {
assertTrue(modified);
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_2));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2, WINDOW_TOKEN_2));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_1));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1, WINDOW_TOKEN_2));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_CONTENT_APP_PROTECTION)
+ public void shouldBlockScreenCaptureForSensitiveContentOnScreenApps() {
+ ArraySet<PackageInfo> blockedApps = new ArraySet(
+ Set.of(new PackageInfo(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1),
+ new PackageInfo(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2)
+ ));
+
+ boolean modified = mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
+ assertTrue(modified);
+
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_2));
+
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_1));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(
+ {FLAG_SENSITIVE_CONTENT_APP_PROTECTION, FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION})
+ public void shouldBlockScreenCaptureForApps() {
+ ArraySet<PackageInfo> blockedApps = new ArraySet(
+ Set.of(new PackageInfo(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1),
+ new PackageInfo(APP_PKG_1, APP_UID_1),
+ new PackageInfo(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2)
+ ));
+
+ boolean modified = mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
+ assertTrue(modified);
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_1));
}
@Test
@@ -92,20 +141,8 @@ public class SensitiveContentPackagesTest {
mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
boolean modified = mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
-
assertFalse(modified);
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ assertTrue(mSensitiveContentPackages.size() == 4);
}
@Test
@@ -121,65 +158,28 @@ public class SensitiveContentPackagesTest {
boolean modified = mSensitiveContentPackages
.addBlockScreenCaptureForApps(
new ArraySet(Set.of(new PackageInfo(APP_PKG_3, APP_UID_1))));
-
assertTrue(modified);
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ assertTrue(mSensitiveContentPackages.size() == 5);
}
@Test
public void clearBlockedApps() {
ArraySet<PackageInfo> blockedApps = new ArraySet(
Set.of(new PackageInfo(APP_PKG_1, APP_UID_1),
- new PackageInfo(APP_PKG_1, APP_UID_2),
- new PackageInfo(APP_PKG_2, APP_UID_1),
- new PackageInfo(APP_PKG_2, APP_UID_2)
+ new PackageInfo(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2)
));
mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
boolean modified = mSensitiveContentPackages.clearBlockedApps();
assertTrue(modified);
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ assertTrue(mSensitiveContentPackages.size() == 0);
}
@Test
public void clearBlockedApps_alreadyEmpty() {
boolean modified = mSensitiveContentPackages.clearBlockedApps();
-
assertFalse(modified);
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ assertTrue(mSensitiveContentPackages.size() == 0);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 73d386a328f5..80fb44a9b50f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -88,8 +88,6 @@ public class WallpaperControllerTests extends WindowTestsBase {
@Test
public void testWallpaperScreenshot() {
- WindowSurfaceController windowSurfaceController = mock(WindowSurfaceController.class);
-
// No wallpaper
final DisplayContent dc = createNewDisplay();
assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
@@ -99,11 +97,9 @@ public class WallpaperControllerTests extends WindowTestsBase {
assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
// Wallpaper with not visible WSA surface.
- wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController;
- wallpaperWindow.mWinAnimator.mLastAlpha = 1;
assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
- when(windowSurfaceController.getShown()).thenReturn(true);
+ makeWallpaperWindowShown(wallpaperWindow);
// Wallpaper with WSA alpha set to 0.
wallpaperWindow.mWinAnimator.mLastAlpha = 0;
@@ -306,14 +302,25 @@ public class WallpaperControllerTests extends WindowTestsBase {
spyOn(mWm.mPolicy);
doReturn(true).when(mWm.mPolicy).isKeyguardLocked();
doReturn(true).when(mWm.mPolicy).isKeyguardOccluded();
- mDisplayContent.mWallpaperController.adjustWallpaperWindows();
+ final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
+ wallpaperController.adjustWallpaperWindows();
// Wallpaper is visible because the show-when-locked activity is translucent.
- assertTrue(mDisplayContent.mWallpaperController.isWallpaperTarget(wallpaperWindow));
+ assertTrue(wallpaperController.isWallpaperTarget(wallpaperWindow));
behind.mActivityRecord.setShowWhenLocked(true);
- mDisplayContent.mWallpaperController.adjustWallpaperWindows();
+ wallpaperController.adjustWallpaperWindows();
// Wallpaper is invisible because the lowest show-when-locked activity is opaque.
- assertTrue(mDisplayContent.mWallpaperController.isWallpaperTarget(null));
+ assertTrue(wallpaperController.isWallpaperTarget(null));
+
+ // A show-when-locked wallpaper is used for lockscreen. So the top wallpaper should
+ // be the one that is not show-when-locked.
+ final WindowState wallpaperWindow2 = createWallpaperWindow(mDisplayContent);
+ makeWallpaperWindowShown(wallpaperWindow2);
+ makeWallpaperWindowShown(wallpaperWindow);
+ assertEquals(wallpaperWindow2, wallpaperController.getTopVisibleWallpaper());
+ wallpaperWindow2.mToken.asWallpaperToken().setShowWhenLocked(true);
+ wallpaperWindow.mToken.asWallpaperToken().setShowWhenLocked(false);
+ assertEquals(wallpaperWindow, wallpaperController.getTopVisibleWallpaper());
}
/**
@@ -527,6 +534,13 @@ public class WallpaperControllerTests extends WindowTestsBase {
assertThat(wallpaperWindow.mYOffset).isEqualTo(0);
}
+ private static void makeWallpaperWindowShown(WindowState w) {
+ final WindowSurfaceController windowSurfaceController = mock(WindowSurfaceController.class);
+ w.mWinAnimator.mSurfaceController = windowSurfaceController;
+ w.mWinAnimator.mLastAlpha = 1;
+ when(windowSurfaceController.getShown()).thenReturn(true);
+ }
+
private WindowState createWallpaperWindow(DisplayContent dc, int width, int height) {
final WindowState wallpaperWindow = createWallpaperWindow(dc);
// Wallpaper is cropped to match first display.
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index c972e518e46d..a0e64bf94393 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -21,9 +21,11 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
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 static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_OWN_FOCUS;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
@@ -80,6 +82,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.util.ArraySet;
import android.util.MergedConfiguration;
import android.view.ContentRecordingSession;
@@ -840,7 +843,8 @@ public class WindowManagerServiceTests extends WindowTestsBase {
}
@Test
- public void addBlockScreenCaptureForApps() {
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
+ public void shouldBlockScreenCaptureForNotificationApps() {
String testPackage = "test";
int ownerId1 = 20;
int ownerId2 = 21;
@@ -850,12 +854,59 @@ public class WindowManagerServiceTests extends WindowTestsBase {
WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
wmInternal.addBlockScreenCaptureForApps(blockedPackages);
+ verify(mWm).refreshScreenCaptureDisabled();
+ // window client token parameter is ignored for this feature.
assertTrue(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId1));
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, new Binder()));
assertFalse(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId2));
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId2, new Binder()));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_CONTENT_APP_PROTECTION)
+ public void shouldBlockScreenCaptureForSensitiveContentOnScreenApps() {
+ String testPackage = "test";
+ int ownerId1 = 20;
+ final IBinder windowClientToken = new Binder("window client token");
+ PackageInfo blockedPackage = new PackageInfo(testPackage, ownerId1, windowClientToken);
+ ArraySet<PackageInfo> blockedPackages = new ArraySet();
+ blockedPackages.add(blockedPackage);
+
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ wmInternal.addBlockScreenCaptureForApps(blockedPackages);
verify(mWm).refreshScreenCaptureDisabled();
+
+ assertTrue(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, windowClientToken));
+ assertFalse(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, new Binder()));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(
+ {FLAG_SENSITIVE_CONTENT_APP_PROTECTION, FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION})
+ public void shouldBlockScreenCaptureForApps() {
+ String testPackage = "test";
+ int ownerId1 = 20;
+ int ownerId2 = 21;
+ final IBinder windowClientToken = new Binder("window client token");
+ PackageInfo blockedPackage1 = new PackageInfo(testPackage, ownerId1);
+ PackageInfo blockedPackage2 = new PackageInfo(testPackage, ownerId1, windowClientToken);
+ ArraySet<PackageInfo> blockedPackages = new ArraySet();
+ blockedPackages.add(blockedPackage1);
+ blockedPackages.add(blockedPackage2);
+
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ wmInternal.addBlockScreenCaptureForApps(blockedPackages);
+ verify(mWm).refreshScreenCaptureDisabled();
+
+ assertTrue(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, windowClientToken));
+ assertTrue(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, new Binder()));
+ assertFalse(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId2, new Binder()));
}
@Test
@@ -891,10 +942,6 @@ public class WindowManagerServiceTests extends WindowTestsBase {
wmInternal.addBlockScreenCaptureForApps(blockedPackages);
wmInternal.addBlockScreenCaptureForApps(blockedPackages2);
- assertTrue(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId1));
- assertTrue(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId2));
verify(mWm, times(2)).refreshScreenCaptureDisabled();
}
@@ -909,9 +956,6 @@ public class WindowManagerServiceTests extends WindowTestsBase {
WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
wmInternal.addBlockScreenCaptureForApps(blockedPackages);
wmInternal.clearBlockedApps();
-
- assertFalse(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId1));
verify(mWm, times(2)).refreshScreenCaptureDisabled();
}
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 7ad26c901188..5ba5ee8836e1 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -169,8 +169,7 @@ public final class DisconnectCause implements Parcelable {
int toneToPlay) {
this(code, label, description, reason, toneToPlay,
android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
- PreciseDisconnectCause.ERROR_UNSPECIFIED,
- null /* imsReasonInfo */);
+ PreciseDisconnectCause.ERROR_UNSPECIFIED, null /* imsReasonInfo */);
}
/**
@@ -187,8 +186,6 @@ public final class DisconnectCause implements Parcelable {
* @param imsReasonInfo The relevant {@link ImsReasonInfo}, or {@code null} if not available.
* @hide
*/
- @SystemApi
- @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
public DisconnectCause(int code, @NonNull CharSequence label,
@NonNull CharSequence description, @NonNull String reason,
int toneToPlay, @Annotation.DisconnectCauses int telephonyDisconnectCause,
@@ -298,6 +295,117 @@ public final class DisconnectCause implements Parcelable {
return mToneToPlay;
}
+ /**
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+ public static final class Builder {
+ private int mDisconnectCode;
+ private CharSequence mDisconnectLabel;
+ private CharSequence mDisconnectDescription;
+ private String mDisconnectReason;
+ private int mToneToPlay;
+ private int mTelephonyDisconnectCause;
+ private int mTelephonyPreciseDisconnectCause;
+ private ImsReasonInfo mImsReasonInfo;
+
+ /**
+ * Sets the code for the reason for this disconnect.
+ * @param code The code denoting the type of disconnect.
+ */
+ public @NonNull DisconnectCause.Builder setCode(int code) {
+ mDisconnectCode = code;
+ return this;
+ }
+
+ /**
+ * Sets a label which explains the reason for the disconnect cause, used for display in the
+ * user interface.
+ * @param label The label to associate with the disconnect cause.
+ */
+ public @NonNull DisconnectCause.Builder setLabel(@Nullable CharSequence label) {
+ mDisconnectLabel = label;
+ return this;
+ }
+
+ /**
+ * Sets a description which provides the reason for the disconnect cause, used for display
+ * in the user interface.
+ * @param description The description to associate with the disconnect cause.
+ */
+ public @NonNull DisconnectCause.Builder setDescription(
+ @Nullable CharSequence description) {
+ mDisconnectDescription = description;
+ return this;
+ }
+
+ /**
+ * Sets a reason providing explanation for the disconnect (intended for logging and not for
+ * displaying in the user interface).
+ * @param reason The reason for the disconnect.
+ */
+ public @NonNull DisconnectCause.Builder setReason(@NonNull String reason) {
+ mDisconnectReason = reason;
+ return this;
+ }
+
+ /**
+ * Sets the tone to play when disconnected.
+ * @param toneToPlay The tone as defined in {@link ToneGenerator} to play when disconnected.
+ */
+ public @NonNull DisconnectCause.Builder setTone(int toneToPlay) {
+ mToneToPlay = toneToPlay;
+ return this;
+ }
+
+ /**
+ * Sets the telephony {@link android.telephony.DisconnectCause} for the call (used
+ * internally by Telecom for providing extra debug information from Telephony).
+ * @param telephonyDisconnectCause The disconnect cause as provided by Telephony.
+ */
+ public @NonNull DisconnectCause.Builder setTelephonyDisconnectCause(
+ @Annotation.DisconnectCauses int telephonyDisconnectCause) {
+ mTelephonyDisconnectCause = telephonyDisconnectCause;
+ return this;
+ }
+
+ /**
+ * Sets the telephony {@link android.telephony.PreciseDisconnectCause} for the call (used
+ * internally by Telecom for providing extra debug information from Telephony).
+ * @param telephonyPreciseDisconnectCause The precise disconnect cause as provided by
+ * Telephony.
+ */
+
+ public @NonNull DisconnectCause.Builder setTelephonyPreciseDisconnectCause(
+ @Annotation.PreciseDisconnectCauses int telephonyPreciseDisconnectCause) {
+ mTelephonyPreciseDisconnectCause = telephonyPreciseDisconnectCause;
+ return this;
+ }
+
+ /**
+ * Returns the telephony {@link ImsReasonInfo} associated with the call disconnection. This
+ * is only used internally by Telecom for providing extra debug information from Telephony.
+ *
+ * @param imsReasonInfo The {@link ImsReasonInfo} or {@code null} if not known.
+ */
+ public @NonNull DisconnectCause.Builder setImsReasonInfo(
+ @Nullable ImsReasonInfo imsReasonInfo) {
+ mImsReasonInfo = imsReasonInfo;
+ return this;
+ }
+
+ /**
+ * Build the {@link DisconnectCause} from the provided Builder config.
+ * @return The {@link DisconnectCause} instance from provided builder.
+ */
+ public @NonNull DisconnectCause build() {
+ return new DisconnectCause(mDisconnectCode, mDisconnectLabel, mDisconnectDescription,
+ mDisconnectReason, mToneToPlay, mTelephonyDisconnectCause,
+ mTelephonyPreciseDisconnectCause, mImsReasonInfo);
+ }
+ }
+
public static final @android.annotation.NonNull Creator<DisconnectCause> CREATOR
= new Creator<DisconnectCause>() {
@Override
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fd9aae9ff835..626a2e574881 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -15014,7 +15014,8 @@ public class TelephonyManager {
* Get the emergency assistance package name.
*
* @return the package name of the emergency assistance app.
- * @throws IllegalStateException if emergency assistance is not enabled.
+ * @throws IllegalStateException if emergency assistance is not enabled or the device is
+ * not voice capable.
*
* @hide
*/
@@ -15023,8 +15024,9 @@ public class TelephonyManager {
@NonNull
@SystemApi
public String getEmergencyAssistancePackage() {
- if (!isEmergencyAssistanceEnabled()) {
- throw new IllegalStateException("isEmergencyAssistanceEnabled() is false.");
+ if (!isEmergencyAssistanceEnabled() || !isVoiceCapable()) {
+ throw new IllegalStateException("isEmergencyAssistanceEnabled() is false or device"
+ + " not voice capable.");
}
String emergencyRole = mContext.getSystemService(RoleManager.class)
.getEmergencyRoleHolder(mContext.getUserId());
diff --git a/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl b/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl
index bdd212afd4b0..e69b60b3a37c 100644
--- a/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl
+++ b/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl
@@ -26,4 +26,5 @@ oneway interface IQualifiedNetworksServiceCallback
{
void onQualifiedNetworkTypesChanged(int apnTypes, in int[] qualifiedNetworkTypes);
void onNetworkValidationRequested(int networkCapability, IIntegerConsumer callback);
+ void onReconnectQualifedNetworkType(int apnTypes, int qualifiedNetworkType);
}
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index c3ba09248298..7bfe04d025c8 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -33,6 +33,7 @@ import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.NetCapability;
import android.telephony.PreciseDataConnectionState;
+import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.SparseArray;
@@ -82,6 +83,7 @@ public abstract class QualifiedNetworksService extends Service {
private static final int QNS_APN_THROTTLE_STATUS_CHANGED = 5;
private static final int QNS_EMERGENCY_DATA_NETWORK_PREFERRED_TRANSPORT_CHANGED = 6;
private static final int QNS_REQUEST_NETWORK_VALIDATION = 7;
+ private static final int QNS_RECONNECT_QUALIFIED_NETWORK = 8;
/** Feature flags */
private static final FeatureFlags sFeatureFlag = new FeatureFlagsImpl();
@@ -186,8 +188,42 @@ public abstract class QualifiedNetworksService extends Service {
qualifiedNetworkTypesArray).sendToTarget();
}
- private void onUpdateQualifiedNetworkTypes(@ApnType int apnTypes,
- int[] qualifiedNetworkTypes) {
+ /**
+ * Request to make a clean initial connection instead of handover to a transport type mapped
+ * to the {@code qualifiedNetworkType} for the {@code apnTypes}. This will update the
+ * preferred network type like {@link #updateQualifiedNetworkTypes(int, List)}, however if
+ * the data network for the {@code apnTypes} is not in the state {@link TelephonyManager
+ * #DATA_CONNECTED} or it's already connected on the transport type mapped to the
+ * qualified network type, forced reconnection will be ignored.
+ *
+ * <p>This will tear down current data network even though target transport type mapped to
+ * the {@code qualifiedNetworkType} is not available, and the data network will be connected
+ * to the transport type when it becomes available.
+ *
+ * <p>This is one shot request and does not mean further handover is not allowed to the
+ * qualified network type for this APN type.
+ *
+ * @param apnTypes APN type(s) of the qualified networks. This must be a bitmask combination
+ * of {@link ApnType}. The same qualified networks will be applicable to all APN types
+ * specified here.
+ * @param qualifiedNetworkType Access network types which are qualified for data connection
+ * setup for {@link ApnType}. Empty list means QNS has no suggestion to the frameworks, and
+ * for that APN type frameworks will route the corresponding network requests to
+ * {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN}.
+ *
+ * <p> If one of the element is invalid, for example, {@link AccessNetworkType#UNKNOWN},
+ * then this operation becomes a no-op.
+ *
+ * @hide
+ */
+ public final void reconnectQualifiedNetworkType(@ApnType int apnTypes,
+ @AccessNetworkConstants.RadioAccessNetworkType int qualifiedNetworkType) {
+ mHandler.obtainMessage(QNS_RECONNECT_QUALIFIED_NETWORK, mSlotIndex, apnTypes,
+ new Integer(qualifiedNetworkType)).sendToTarget();
+ }
+
+ private void onUpdateQualifiedNetworkTypes(
+ @ApnType int apnTypes, int[] qualifiedNetworkTypes) {
mQualifiedNetworkTypesList.put(apnTypes, qualifiedNetworkTypes);
if (mCallback != null) {
try {
@@ -198,6 +234,17 @@ public abstract class QualifiedNetworksService extends Service {
}
}
+ private void onReconnectQualifiedNetworkType(@ApnType int apnTypes,
+ @AccessNetworkConstants.RadioAccessNetworkType int qualifiedNetworkType) {
+ if (mCallback != null) {
+ try {
+ mCallback.onReconnectQualifedNetworkType(apnTypes, qualifiedNetworkType);
+ } catch (RemoteException e) {
+ loge("Failed to call onReconnectQualifiedNetworkType. " + e);
+ }
+ }
+ }
+
/**
* The framework calls this method when the throttle status of an APN changes.
*
@@ -366,6 +413,12 @@ public abstract class QualifiedNetworksService extends Service {
case QNS_REQUEST_NETWORK_VALIDATION:
if (provider == null) break;
provider.onRequestNetworkValidation((NetworkValidationRequestData) message.obj);
+ break;
+
+ case QNS_RECONNECT_QUALIFIED_NETWORK:
+ if (provider == null) break;
+ provider.onReconnectQualifiedNetworkType(message.arg2, (Integer) message.obj);
+ break;
}
}
}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index f628af14a0b9..452c98c65a7f 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -296,6 +296,10 @@ open class PipAppHelper(instrumentation: Instrumentation) :
clickObject(MEDIA_SESSION_START_RADIO_BUTTON_ID)
}
+ fun setSourceRectHint() {
+ clickObject(SOURCE_RECT_HINT)
+ }
+
fun checkWithCustomActionsCheckbox() =
uiDevice
.findObject(By.res(packageName, WITH_CUSTOM_ACTIONS_BUTTON_ID))
@@ -444,6 +448,7 @@ open class PipAppHelper(instrumentation: Instrumentation) :
private const val MEDIA_SESSION_START_RADIO_BUTTON_ID = "media_session_start"
private const val ENTER_PIP_ON_USER_LEAVE_HINT = "enter_pip_on_leave_manual"
private const val ENTER_PIP_AUTOENTER = "enter_pip_on_leave_autoenter"
+ private const val SOURCE_RECT_HINT = "set_source_rect_hint"
// minimum number of steps to take, when animating gestures, needs to be 2
// so that there is at least a single intermediate layer that flicker tests can check
private const val MIN_STEPS_TO_ANIMATE = 2
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
index f7ba45b25d48..36cbf1a8fe84 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
@@ -27,6 +27,15 @@
where things are arranged differently and to circle back up to the top once we reach the
bottom. -->
+ <!-- View used for testing sourceRectHint. -->
+ <View
+ android:id="@+id/source_rect"
+ android:layout_width="320dp"
+ android:layout_height="180dp"
+ android:visibility="gone"
+ android:background="@android:color/holo_green_light"
+ />
+
<Button
android:id="@+id/enter_pip"
android:layout_width="wrap_content"
@@ -113,6 +122,13 @@
android:onClick="onRatioSelected"/>
</RadioGroup>
+ <Button
+ android:id="@+id/set_source_rect_hint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Set SourceRectHint"
+ android:onClick="setSourceRectHint"/>
+
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
index 12eaad108fc6..1ab8ddbe20e2 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -37,6 +37,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
import android.media.session.MediaSession;
@@ -45,6 +46,7 @@ import android.os.Bundle;
import android.util.Log;
import android.util.Rational;
import android.view.View;
+import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.widget.CheckBox;
@@ -248,6 +250,29 @@ public class PipActivity extends Activity {
}
}
+ /**
+ * Adds a temporary view used for testing sourceRectHint.
+ *
+ */
+ public void setSourceRectHint(View v) {
+ View rectView = findViewById(R.id.source_rect);
+ if (rectView != null) {
+ rectView.setVisibility(View.VISIBLE);
+ rectView.getViewTreeObserver().addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ Rect boundingRect = new Rect();
+ rectView.getGlobalVisibleRect(boundingRect);
+ mPipParamsBuilder.setSourceRectHint(boundingRect);
+ setPictureInPictureParams(mPipParamsBuilder.build());
+ rectView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ }
+ });
+ rectView.invalidate(); // changing the visibility, invalidating to redraw the view
+ }
+ }
+
public void onRatioSelected(View v) {
switch (v.getId()) {
case R.id.ratio_default:
diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp
index 228520e8545b..ee2e7cfcd480 100644
--- a/tests/vcn/Android.bp
+++ b/tests/vcn/Android.bp
@@ -3,6 +3,7 @@
//########################################################################
package {
+ default_team: "trendy_team_enigma",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
@@ -30,6 +31,7 @@ android_test {
"platform-test-annotations",
"services.core",
"service-connectivity-tiramisu-pre-jarjar",
+ "flag-junit",
],
libs: [
"android.test.runner",
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 34f884b94296..887630b03a8c 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -38,6 +38,7 @@ import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
@@ -58,6 +59,7 @@ import android.os.HandlerExecutor;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.test.TestLooper;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -71,7 +73,10 @@ import android.util.ArraySet;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.telephony.flags.Flags;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -128,6 +133,9 @@ public class TelephonySubscriptionTrackerTest {
TEST_SUBID_TO_CARRIER_CONFIG_MAP = Collections.unmodifiableMap(subIdToCarrierConfigMap);
}
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+
@NonNull private final Context mContext;
@NonNull private final TestLooper mTestLooper;
@NonNull private final Handler mHandler;
@@ -185,6 +193,7 @@ public class TelephonySubscriptionTrackerTest {
@Before
public void setUp() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_FIX_CRASH_ON_GETTING_CONFIG_WHEN_PHONE_IS_GONE);
doReturn(2).when(mTelephonyManager).getActiveModemCount();
mCallback = mock(TelephonySubscriptionTrackerCallback.class);
@@ -594,4 +603,14 @@ public class TelephonySubscriptionTrackerTest {
new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)),
snapshot.getAllSubIdsInGroup(TEST_PARCEL_UUID));
}
+
+ @Test
+ public void testCarrierConfigChangeWhenPhoneIsGoneShouldNotCrash() throws Exception {
+ doThrow(new IllegalStateException("Carrier config loader is not available."))
+ .when(mCarrierConfigManager)
+ .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1), any());
+
+ sendCarrierConfigChange(true /* hasValidSubscription */);
+ mTestLooper.dispatchAll();
+ }
}
diff --git a/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt b/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt
index 451e514b8b33..07c6fd328318 100644
--- a/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt
@@ -16,6 +16,7 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.ast.CompilationUnit
import com.github.javaparser.ast.ImportDeclaration
import com.github.javaparser.ast.expr.BinaryExpr
diff --git a/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt b/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt
deleted file mode 100644
index e88f0f8231bd..000000000000
--- a/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.protolog.tool
-
-import com.github.javaparser.ast.Node
-
-enum class LogLevel {
- DEBUG, VERBOSE, INFO, WARN, ERROR, WTF;
-
- companion object {
- fun getLevelForMethodName(name: String, node: Node, context: ParsingContext): LogLevel {
- return when (name) {
- "d" -> DEBUG
- "v" -> VERBOSE
- "i" -> INFO
- "w" -> WARN
- "e" -> ERROR
- "wtf" -> WTF
- else ->
- throw InvalidProtoLogCallException("Unknown log level $name in $node", context)
- }
- }
- }
-}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
index 2181cf680f6c..9a76a6f84c2b 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
@@ -16,7 +16,9 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.ast.CompilationUnit
+import com.github.javaparser.ast.Node
import com.github.javaparser.ast.expr.Expression
import com.github.javaparser.ast.expr.FieldAccessExpr
import com.github.javaparser.ast.expr.MethodCallExpr
@@ -105,9 +107,24 @@ open class ProtoLogCallProcessor(
"- not a ProtoLogGroup enum member: $call", context)
}
- callVisitor?.processCall(call, messageString, LogLevel.getLevelForMethodName(
+ callVisitor?.processCall(call, messageString, getLevelForMethodName(
call.name.toString(), call, context), groupMap.getValue(groupName))
}
return code
}
+
+ companion object {
+ fun getLevelForMethodName(name: String, node: Node, context: ParsingContext): LogLevel {
+ return when (name) {
+ "d" -> LogLevel.DEBUG
+ "v" -> LogLevel.VERBOSE
+ "i" -> LogLevel.INFO
+ "w" -> LogLevel.WARN
+ "e" -> LogLevel.ERROR
+ "wtf" -> LogLevel.WTF
+ else ->
+ throw InvalidProtoLogCallException("Unknown log level $name in $node", context)
+ }
+ }
+ }
}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
index aa58b69d61cb..8cd927a7cd0e 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
@@ -16,6 +16,7 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.ast.expr.MethodCallExpr
interface ProtoLogCallVisitor {
diff --git a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
index 27e61a139451..b50f357c57ec 100644
--- a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
@@ -17,6 +17,7 @@
package com.android.protolog.tool
import com.android.internal.protolog.common.LogDataType
+import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.CompilationUnit
import com.github.javaparser.ast.NodeList
diff --git a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt
index 175c71ff810b..0d5d022959b2 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt
@@ -16,6 +16,7 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
import com.android.json.stream.JsonWriter
import com.github.javaparser.ast.CompilationUnit
import com.android.protolog.tool.Constants.VERSION
diff --git a/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt b/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt
index b916f8f00a68..b08d859bad32 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt
@@ -16,6 +16,7 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.expr.BinaryExpr
import com.github.javaparser.ast.expr.StringLiteralExpr
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt
index 97f67a0a3fdb..90b8059dae1c 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt
@@ -16,6 +16,7 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.expr.MethodCallExpr
import org.junit.Assert.assertEquals
diff --git a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
index 4f2be328fc8a..f52bfeccea51 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
@@ -16,6 +16,7 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.CompilationUnit
import com.github.javaparser.ast.expr.MethodCallExpr
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt
index a24761aed9db..52dce21944f6 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt
@@ -16,6 +16,7 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
import com.android.json.stream.JsonReader
import com.android.protolog.tool.ViewerConfigBuilder.LogCall
import org.junit.Assert.assertEquals
diff --git a/wifi/TEST_MAPPING b/wifi/TEST_MAPPING
index 757ecaa8aca9..3ae91bee6f45 100644
--- a/wifi/TEST_MAPPING
+++ b/wifi/TEST_MAPPING
@@ -2,9 +2,7 @@
"presubmit": [
{
"name": "FrameworksWifiNonUpdatableApiTests"
- }
- ],
- "postsubmit": [
+ },
{
"name": "CtsWifiNonUpdatableTestCases"
}